In order to improve the response speed of a single page application, we usually use webpack import to dynamically import routes. So how does Webpack load the code on demand and put it into the Module?

I can imagine loading a script tag dynamically, but executing it affects the global environment. If the contents of a dynamically loaded script are wrapped in an immediately executed function, how can other modules use it to fetch the exported variables of the module (the two IIFE functions are isolated from each other)? If you consider using A to load B on demand and B to load C at the same time, it is very difficult to have A look at the implementation of Webpack with the problem:

Start with a simple webapck configuration (webpack5.0 is used in this article, and NPX is used to run the package build)

const path = require('path');
module.exports = {
    entry: {
        'index': './src/index.js'
    },
    output: {
        path: path.resolve(__dirname, './dist'),
        filename: '[name].js'
    },
    module: {
        rules: [{test: /\.js$/,
                use: 'babel-loader'}},plugins: [].mode: 'none' // Set to None to prevent code from being uglify
}
Copy the code

Let’s start with the packaged structure

(function () = >{
    var __webpack_modules__ = [];
    var __webpack_module_cache__ = {};
    function __webpack_require__ (moduleId) {};// The following three attribute definitions are wrapped in IIFE functions in the compiled code; I've left it out for visual convenience
    __webpack_require__.d = (export, defination) = > {}; 
    __webpack_require__.o = (obj, prop) = > (Object.prototype.hasOwnProperty.call(obj, prop));
    __webpack_require__.r = (exports) = > {};// Set the __esModule property to true on the exports object;
    var __webpack_exports__ = {};
    (() = > {
        __webpack_require__.r(__webpack_exports__);
        Import {add} from './add'
        var _add__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1); 
        var _multiple__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(2);

        // dosomething(); // Entry module code;
        const setAddThenMulti = (a, b) = > m= >{(0,_multiple__WEBPACK_IMPORTED_MODULE_1__.multiple)((0,_add__WEBPACK_IMPORTED_MODULE_0__.add)(a + b), m); }; }) ();// Execute the entry module code immediately}) ()Copy the code

Modules and require member structures

The __webpack_require__ function, the whole logic is to return the cached module if it has been loaded, otherwise the module will be loaded and added to the cache, and return the hanging module access object

function __webpack_require__ (moduleId) {
    var cachedModule = __webpack_module_cache__[moduleId];
    if(cachedModule ! = =undefined) {
        return cachedModule.exports;
    }
    var module = __webpack_module_cache__[moduleId] = {
        exports: {}}; __webpack_modules__[moduleId](module.module.exports, __webpack_require__);
    return module.exports;
}
Copy the code

The member structure in __webpack_modules__ is as follows:

((__unused_webpack_module, __webpack_exports__, __webpack_require__) = > {
    __webpack_require__.r(__webpack_exports__);
    // Here is more important; Hangs corresponding properties on exports; Export const add before packaging
    __webpack_require__.d(__webpack_exports__, {
        "add": () = > (/* binding */ add)
    });
    const add = (a, b) = > a + b;
})
Copy the code

Through the above we learned a very simple module loading process;

First webPack puts non-entry modules into the Modules array; And then when you need it, you load it through require; And only load once; Cache will follow; So how does an on-demand module enter this process?

Let’s look again at the introduction of import() into the code; After packaging, it is found that the imported modules are placed in a separate file. And the packaged original file also has the following properties on the require function:

__webpack_require__.f = {};
__webpack_require__.e = (chunkId) = > {}; // Return a Promise
__webpack_require__.u = () = > {}; // Generate the file name of the asynchronous module
__webpack_require__.m = __webpack_modules__; // Expose modules access
__webpack_require__.g = (() = >{}) (); (() = > {
    var inProgress = {};
    __webpack_require__.l = (url, done, key, chunkId) = > {} // Dynamically create the script tag to load the module, which does the logic that if the module is loaded, it will not be loaded again}) (); (() = > { // Provide the path to load the module and concatenate __webpack_require__.u with the SRC that eventually generates the script
    var scriptUrl;
    // dosomething;__webpack_require__.p = scriptUrl; }) (); (() = > {
    var installedChunks = {0: 0}; // 0 means chunked is loaded; The format in load is {'chunckId': [resolve, reject, promise]}
    __webpack_require__.f.j = (chunkId, promises) = > {}
})();
var webpackJsonpCallback = (parentChunkLoadingFunction, data) = > {};
var chunkLoadingGlobal = self["webpackChunk"] = self["webpackChunk"] | | []; chunkLoadingGlobal.forEach(webpackJsonpCallback.bind(null.0));
chunkLoadingGlobal.push = webpackJsonpCallback.bind(null, chunkLoadingGlobal.push.bind(chunkLoadingGlobal));
Copy the code

The following is the introduction departure process: E (/* import() */ 1).then(_webpack_require_.bind(_webpack_require_, 2));

_webpack_require_.e returns a promise.all() state that will be resolved after all modules have been loaded. The code loaded via script will call the global webpackJsonpCallback function to communicate with internal modules. Call push to add yourself to modules; Then __webpack_require__ is used to retrieve the exported object of the loaded code module.

WebpackJsonpCallback code

var webpackJsonpCallback = (parentChunkLoadingFunction, data) = > {
    var [moduleIds, moreModules, runtime] = data;
    var moduleId, chunkId, i = 0;
    for (moduleId in moreModules) {
        if (__webpack_require__.o(moreModules, moduleId)) {
            __webpack_require__.m[moduleId] = moreModules[moduleId]; // Module installation logic}}if(runtime) runtime(__webpack_require__); // Do not write comments for now
    if(parentChunkLoadingFunction) parentChunkLoadingFunction(data);// Do not write comments for now
    for(; i < chunkIds.length; i++) { chunkId = chunkIds[i];if(__webpack_require__.o(installedChunks, chunkId) && installedChunks[chunkId]) {
            installedChunks[chunkId][0] ();// The logic of the promise state was searched for a while
        }
        installedChunks[chunkIds[i]] = 0; // Marked as loaded}}Copy the code

The code for the module that is loaded on demand

(self["webpackChunk"] = self["webpackChunk"] || []).push(
    [[1] and {2:        
        ((__unused_webpack_module, __webpack_exports__, __webpack_require__) = > {
                __webpack_require__.r(__webpack_exports__);
                __webpack_require__.d(__webpack_exports__, {
        "multiple": () = > (/* binding */ multiple)
        });
        const multiple = (a, b) = >a * b; }}));Copy the code

The whole process line should be sorted out so much first, and the more complex scenes and ununderstood logic should be further studied.

Look at the under-used apis in your code

Symbol. ToStringFlag adds type identification attributes to objects [developer.mozilla.org/zh-CN/docs/…]