preface

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

What does this API do for us? It helps us dynamically load the files we want, very flexible and powerful (recursive directory). You can do things that import cannot. Webpack’s require.context is implemented in webpack.

The preparatory work

Before we look at the API, we need to take a look at the simplest file, webPack, that compiles to look like this.

-- src
    -- index.ts
Copy the code
// index.ts
console.log(123)
Copy the code

After compiling, we can see that WebPack compiles into the following code

/ / source https://github.com/MeCKodo/require-context-sourece/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'); })});Copy the code

It’s messy at first glance, so I’m going to remove some of the things that are irrelevant to this article in order to sort out the structure. But that’s the main structure, there’s not a lot of code and for the sake of understanding it later, make sure you look at every line

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

(function(modules) {
  // Cache all loaded modules (files)
  var installedModules = {};
  // The moduleId of the module (file) loader is generally the file path
  function __webpack_require__(moduleId) {
    / / the 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 our module (file) which is currently./ SRC /index.ts and pass 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 the entry file
  return __webpack_require__((__webpack_require__.s = './src/index.ts')); ({})'./src/index.ts': function(module, exports, __webpack_require__) {
    console.log('123'); }});Copy the code

__webpack_require__ is a module loader, and all of our modules are read and loaded as objects

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

We’ll call such structures a module structure object for now

positive

Once we know the body structure, we can write a require.context to see how it works. Let’s add two new ts files and modify our index.ts to test our dynamic loading.

--- src
    --- demos
        --- demo1.ts
        --- demo2.ts
    index.ts
Copy the code
// index.ts
// Why do I 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);
Copy the code

Look at our compiled source code, found more such a module structure object

/ / the compiled code address https://github.com/MeCKodo/require-context-sourece/blob/master/simple-dist/contex-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'
  };

  // The context loader loads the module (file) through the previous module loader.
  function webpackContext(req) {
    var id = webpackContextResolve(req);
    var module = __webpack_require__(id);
    return module;
  }
  
  // Find the real path of the module (file) using the moduleId
  // I don't like some variable names inside Webpack, moduleId, which will compile to Request
  function webpackContextResolve(req) {
    // id is the real file path
    var id = map[req];
    // Webpack will compile to 0. Js 1. Js file if not found, error will be generated
    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;
  }
  
  // Iterate to get all moduleids
  webpackContext.keys = function webpackContextKeys() {
    return Object.keys(map);
  };
  // Get the real path of the file
  webpackContext.resolve = webpackContextResolve;
  // This module returns a context loader
  module.exports = webpackContext;
  // The moduleId of this module is used for the __webpack_require__ module loader
  webpackContext.id = './src/demos sync recursive \\.ts';
}
Copy the code

I have written very detailed comments in the source code. Context returns a function with three apis (webpackContext).

Then we look at the compiled source code for index.ts

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

Context is compiled to the __webpack_require__ loader and loaded with a module with id./ SRC /demos sync recursive \\. Ts. Sync indicates that we are loading these modules synchronously (we’ll cover this parameter later) and recursive indicates that we need a recursive directory lookup. From there, we can fully understand how WebPack builds all modules and loads them dynamically.

Further delve into the Webpack source code

We know that after WebPack 2.6, when loading modules, we can specify the webpackMode module loading mode, we can use several ways to control the module we want to load. The common mode is sync lazy lazy-once eager

So require.context is also true, and if we look at @types/webpack-env it has a fourth argument.

Briefly,

  • syncPackage directly to the current file, load and execute synchronously
  • lazyLazy loading separates separate chunk files
  • lazy-onceLazy loading splits the chunks into separate files that are loaded and loaded next time to read the code directly from memory.
  • eagerNo separate chunk files will be separated, but the promise will be returned, and the code will be executed only after the promise is invoked. It can be understood that the code is loaded first, but we can control the delay in executing this part of the code.

Document webpack.docschina.org/api/module- here…

This part of the documentation is very obscure, or maybe the documentation group is not keeping up, so if we look at the source code for WebPack, we can see that there are really six modes.

Mode type definition github.com/webpack/web…

So how does WebPack recursively retrieve our files? In the source address above we can find this line of code.

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

This is the logic of how require.context is loaded into our file. It’s just fs.readdir. Finally, after we get the file, we use the Context loader to generate our module structure object. For example, this code is responsible for generating our Sync-type context loader. You can look at the other 5 types.

6 types of load logic and generate the module structure object of the context loader github.com/webpack/web…

conclusion

1. Learn how WebPack organizes the loading of a module, how the WebPack loader works, and how compiled code is generated.

2. I just want to know how require.context is implemented, but I find that its third parameter has 6 modes, which is also not included in the Webpack document.

3. Started from a practical API, explored the implementation principle of the API, and read part of webPack source code together.

4. Exploring the nitty gritty is far more important than being an API porter. You can discover the secrets of the world only if you keep searching for the essence.

Finally, we can learn the compiled code of the other six modes according to this idea.

The compiled code in the article is here >>> github.com/MeCKodo/req…

Personal website >>> www.meckodo.com

Finally year-round recruitment

Xiamen RingCentral foreign enterprise, welfare treatment xiamen top

I get off work at 5:30. I get off work at 5:30

Contact me if you need me