In depth understanding of the requirement.context of webpack

Time:2020-3-18

Preface

Require.context is actually a very practical API. But after 3-4 years, there are still many people who don’t know how to use it.

And what does this API do for us?It can help us dynamically load the files we want, which is very flexible and powerful (recursive directory).You can do things that import can’t. Let’s take you to analyze today, webback’srequire.contextHow to achieve it.

Preparation

Before analyzing the API, we need to know the simplest file, and what kind of Web pack will be compiled into.

-- src
    -- index.ts
// index.ts
console.log(123)

After compiling, we can see that webpack will be compiled into the following code

//Source https://github.com/meckodo/require-context-source/blob/master/simple-dist/bundle-only-index.js
 (function(modules) { // webpackBootstrap
     // The module cache
     var installedModules = {};
     // The require function
     function __webpack_require__(moduleId) {
         // Check if module is in cache
         if(installedModules[moduleId]) {
             return installedModules[moduleId].exports;
         }
         // Create a new module (and put it into the cache)
         var module = installedModules[moduleId] = {
             i: moduleId,
             l: false,
             exports: {}
         };
         // Execute the module function
         modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
         // Flag the module as loaded
         module.l = true;
         // Return the exports of the module
         return module.exports;
     }
     // expose the modules object (__webpack_modules__)
     __webpack_require__.m = modules;
     // expose the module cache
     __webpack_require__.c = installedModules;
     // define getter function for harmony exports
     __webpack_require__.d = function(exports, name, getter) {
         if(!__webpack_require__.o(exports, name)) {
             Object.defineProperty(exports, name, {
                 configurable: false,
                 enumerable: true,
                 get: getter
             });
         }
     };
     // define __esModule on exports
     __webpack_require__.r = function(exports) {
         Object.defineProperty(exports, '__esModule', { value: true });
     };
     // getDefaultExport function for compatibility with non-harmony modules
     __webpack_require__.n = function(module) {
         var getter = module && module.__esModule ?
             function getDefault() { return module['default']; } :
             function getModuleExports() { return module; };
         __webpack_require__.d(getter, 'a', getter);
         return getter;
     };
     // Object.prototype.hasOwnProperty.call
     __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
     // __webpack_public_path__
     __webpack_require__.p = "";
     // Load entry module and return exports
     return __webpack_require__(__webpack_require__.s = "./src/index.ts");
 })
 ({
 "./src/index.ts": (function(module, exports) {
      console.log('123');
    })
 });

At first glance, it’s very messy, so in order to sort out the structure, I’ll help you to remove something irrelevant to this article. In fact, this is the main structure,There is not much code for later understanding, so you must carefully look at each line

//Source address: https://github.com/mechodo/require-context-source/blob/master/simple-dist/webpack-main.js

(function(modules) {
  //Cache all loaded modules (files)
  var installedModules = {};
  //Module (file) loader moduleid is generally the file path
  function __webpack_require__(moduleId) {
    // go cache
    if (installedModules[moduleId]) {
      return installedModules[moduleId].exports;
    }
    //Create a new module (and put it into the cache)
    var module = (installedModules[moduleId] = {
      i: moduleId,
      l: false,
      exports: {}
    });
    //Executing our module (file) is. / SRC / index.ts and passing in three parameters
    modules[moduleId].call(
      module.exports,
      module,
      module.exports,
      __webpack_require__
    );
    //Flag the module as loaded
    module.l = true;
    //Return the exports of the module
    return module.exports;
  }
  //Start loading entry file
  return __webpack_require__((__webpack_require__.s = './src/index.ts'));
})({
  './src/index.ts': function(module, exports, __webpack_require__) {
    console.log('123');
  }
});

__webpack_require__It is a module loader, and all our modules will be read and loaded in the form of objects

modules = {
    './src/index.ts': function(module, exports, __webpack_require__) {
       console.log('123');
    }
}

We call this structure temporarilyModule structure object

Positive

Once we understand the main structure, we can write a paragraphrequire.contextLet’s see the effect. Let’s add 2 ts files and modify our index.ts to test our dynamic loading.

--- src
    --- demos
        --- demo1.ts
        --- demo2.ts
    index.ts
// index.ts
//Later, we will analyze the source code to see why we write this way
function importAll(contextLoader: __WebpackModuleApi.RequireContext) {
  contextLoader.keys().forEach(id => console.log(contextLoader(id)));
}

const contextLoader = require.context('./demos', true, /\.ts/);
importAll(contextLoader);

Check our compiled source code and find that there is moreModule structure object

//Compiled code address: https://github.com/mechodo/require-context-source/blob/master/simple-dist/context-sync.js × l82-l113
{
'./src/demos sync recursive \\.ts': function(module, exports, __webpack_require__) {
  var map = {
    './demo1.ts': './src/demos/demo1.ts',
    './demo2.ts': './src/demos/demo2.ts'
  };

  //Context loader, load module (file) through previous module loader 
  function webpackContext(req) {
    var id = webpackContextResolve(req);
    var module = __webpack_require__(id);
    return module;
  }
  
  //Find the real path of module (file) through moduleid
  //I don't like the naming of some variables in webpack. Moduleid will be compiled as request
  function webpackContextResolve(req) {
    //ID is the real file path
    var id = map[req];
    //To be honest, I didn't understand this wave of operations. At present, I guess that webpack will compile into a file like 0.js 1.js. If it can't find the wrong load, an error will occur
    if (!(id + 1)) {
      // check for number or string
      var e = new Error('Cannot find module "' + req + '".');
      e.code = 'MODULE_NOT_FOUND';
      throw e;
    }
    return id;
  }
  
  //Traverse to get all moduleid
  webpackContext.keys = function webpackContextKeys() {
    return Object.keys(map);
  };
  //Getting the real path of files
  webpackContext.resolve = webpackContextResolve;
  //This module returns a context loader
  module.exports = webpackContext;
  //The moduleid of the module is used for the webpack require module loader
  webpackContext.id = './src/demos sync recursive \\.ts';
}

I wrote detailed comments in the source code. It’s not hard to understand what the document says after reading this coderequire.contextA function (webbackcontext) with three APIs will be returned.

In depth understanding of the requirement.context of webpack

Then let’s look at the compiledindex.tsSource code

'./src/index.ts': function(module, exports, __webpack_require__) {
  function importAll(contextLoader) {
    contextLoader.keys().forEach(function(id) {
      //Get all the moduleid and load each module through the context loader
      return console.log(contextLoader(id));
    });
  }
  var contextLoader = __webpack_require__(
    './src/demos sync recursive \\.ts'
  );
  importAll(contextLoader);
}

It’s easy to seerequire.contextCompile for__webpack_require__Loader and loaded with ID./src/demos sync recursive \\.tsModule,syncIndicates that we are loading these modules synchronously (we will introduce this parameter later),recursiveIndicates that recursive directory lookup is required. From then on, we can fully understand how webback builds all the modules and loads them dynamically.

Advanced in-depth exploration of webpack source code

We know that after version 2.6 of webpack, when loading modules, you can specifywebpackModeModule loading mode, we can use several ways to control the modules we want to load. Commonly used modes aresync lazy lazy-once eager

So it’s the same in require.context. Let’s check it out@types/webpack-envIt’s not hard to see that it has a fourth parameter.

In depth understanding of the requirement.context of webpack

Briefly

  • syncPackage directly to the current file, load and execute synchronously
  • lazyDelayed loading separates the individual chunk files
  • lazy-onceDelayed loading will separate the individual chunk file and load the code in memory directly after the next load.
  • eagerThe separate chunk file will not be separated, but promise will be returned. Only promise is called can the code be executed. It can be understood that the code is loaded first, but we can control the delayed execution of this part of the code.

The document is here at https://webpack.docschina.org.

This part of the document is very obscure, or the document group may not keep up with it, so if we look at the source code of webpack, we can find that there are actually six modes.

Mode type definition
https://github.com/webpack/we…

So how does webback get our files recursively? We can find such a line of code in the source address just above.

In depth understanding of the requirement.context of webpack

This view is to find the modules we need. So we follow this line to find the specific source code.

In depth understanding of the requirement.context of webpack

This is the specific logic of how to load require.context into our file. In fact, that isfs.readdirNothing more. Finally, after getting the file, we generate our module structure object through the context loader. For example, such code is responsible for generating ussyncContext loader of type. You can see the other five types.

In depth understanding of the requirement.context of webpack

Six types of module structure objects that load logic and generate context loader
https://github.com/webpack/we…

summary

1. Learn how to organize and load a module, how to operate the loader of webpack, and how to generate compiled code.

2. I just wanted to knowrequire.contextHow to implement it? We found that there are six modes for its third parameter, which are not found in the web pack document.

3. Starting from a practical API, we explore the implementation principle of the API, and read part of the web pack source code together.

4.It’s far more important to explore the essence than to be an API porter. Only when you constantly explore the essence can you discover the mystery of the world.

In the end, we can learn another six modes of compiled code according to this idea.

The compiled code in this article is here > > > https://github.com/meckodo/re

Personal website > > > http://www.meckodo.com

Last year recruitment

Xiamen ringcentral, a foreign enterprise, is one of the top welfare companies in Xiamen

In depth understanding of the requirement.context of webpack

5:30 off work 5:30 off work 5:30 off work

Add me wechat abcdefghijklmno kodo with ideas