• Module loading in Webpack can be synchronous or asynchronous
    • The core principle of asynchronous loading is through JSONP
  • Webpack module loading is okcommonjs modulees moduleA mixture of
    • Import and export in code no matter how it is packaged and compiled, is itcommonjs moduleway

The following packaged and compiled code example, the source code looks like this:

// src/index.js
let title = require('./src/title.js');
console.log(title);
Copy the code
// src/title.js
module.exports = 'title';
Copy the code

basic

Simple import and export of modules via commonJS Module. Webpack is shortened as follows:

(() = >{
  var modules = {
    './src/title.js':(module.exports.require) = >{
      module.exports = 'title'; }}var cache = {};
  function require(moduleId){
    if(cache[moduleId]){// See if there are any module objects in the cache
      return cache[moduleId].exports;// If yes, return directly
    }
    Module. exports defaults to an empty object
    var module = {exports: {}}; cache[moduleId]=module;
    Module.exports is assigned a value when the module's code executes
    modules[moduleId].call(module.exports,module.module.exports,require);
    return module.exports;
  }
  / /. / SRC/index. Js code
  (() = >{
    let title = require('./src/title.js');
    console.log(title); }) (); }) ();Copy the code

Create a Modules object to store the module to be loaded, define the require function to pass in the module ID, use the module ID to find the corresponding module function in the modules object, and call it in the way of call. Module and module.exports bind the return value of the current module. Next, execute the code in index.js to call the require function.

common-load-es

D and require.r. R is used to add the Symbol. ToStringTag attribute to exports and set the [Object Module] type. Exports also added an __esModule property set to true.

Module.exports.default module.exports.xxx = commonjs module.exports.default module.exports.xxx

(() = >{
  var modules = {
    // To summarize, how does an es module become commonJS
    //export default becomes exports.default
    //export xx exports.xx
    './src/title.js':(module.exports.require) = >{
      // Both commonjs and ES modules compile to common.js.
      /* exports.__esModule=true; /* exports.__esModule=true
      require.r(exports);
      require.d(exports, {default:() = >DEFAULT_EXPORT,
        age:() = >age
      });
      const DEFAULT_EXPORT = 'title_name';
      const age = 'title_age'; }}function require(moduleId){... }require.d = (exports,definition) = >{
    for(let key in definition){
      //exports[key]=definition[key]();
      Object.defineProperty(exports,key,{enumerable:true.get:definition[key]}); }}require.r = (exports) = >{
    //console.log(Object.prototype.toString.call(exports)); //[object Module]
    //exports.__esModule=true 
    Object.defineProperty(exports.Symbol.toStringTag,{value:'Module'});
    Object.defineProperty(exports.'__esModule', {value:true});
  }
  / /. / SRC/index. Js code
  (() = >{
    let title = require('./src/title.js');
    console.log(title);
    console.log(title.age); }) (); }) ();Copy the code

es-load-es

The code packaged on common-load-es turns index.js into a module to be loaded in modules objects and executes require.r to indicate that the current index.js module is an ES module

(() = >{
  var modules = {
    "./src/index.js":
      ((module.exports.require) = > {
        require.r(exports);// This is an es module
        var title = require("./src/title.js");
        console.log(title.default);
        console.log(title.age);
      }),
    './src/title.js':(module.exports.require) = >{
      require.r(exports);
      require.d(exports, {default:() = >DEFAULT_EXPORT,
        age:() = >age
      });
      const DEFAULT_EXPORT = 'title_name';
      const age = 'title_age'; }}function require(moduleId){... }require.d = (exports,definition) = >{... }require.r = (exports) = >{... }/ /. / SRC/index. Js code
  (() = >{
    require("./src/index.js"); }) (); }) ();Copy the code

es-loda-common

A new require. N function is added to the packaged code to determine whether the export is commonJS Module or ES Module

(() = >{
  var modules = {
    "./src/index.js":
      ((module.exports.require) = > {
        require.r(exports);// This is an es module
        // require
        var title = require("./src/title.js");
        I don't know if title.js is an es module or a common module
        var title_default = require.n(title);
        console.log((title_default()));/ / the default value
        console.log(title.age);
      }),
    './src/title.js':(module.exports.require) = >{
      module.exports = {
        name: 'title_name'.age: 'title_age'}}}function require(moduleId){... }require.n = (exports) = >{
    var getter = exports.__esModule?() = >exports.default:() = >exports;
    return getter;
  }
  require.d = (exports,definition) = >{... }require.r = (exports) = >{... }/ /. / SRC/index. Js code
  (() = >{
    require("./src/index.js"); }) (); }) ();Copy the code

Asynchronous loading module

Before we do that, let’s talk about the concept of chunk of code in Webpack. If you load modules asynchronously through import, they are packaged into a single JS file during packaging (a single chunk of code), i.e. not packaged into main.js!

For example, if module A is the entry and module A synchronously introduces modules B and C, then A, B and C will be packaged into A code block (main.js). For example, module A asynchronously introduces module C and module C synchronously introduces module E. Js and c.js, A, B and C in the same block, C and E in the same block, if the C module is asynchronous in the E module, this will produce three code blocks…

Take the following source code for example:

// src/index.js
import(/* webpackChunkName: "hello" */'./hello').then(result= >{
      console.log(result.default);
})
Copy the code
// src/hello.js
module.exports = 'hello ';
Copy the code

The package generates two JS files, main.js and hello.js

Packaged and compiled:

// dist/main.js
(() = >{
  // Hold all module definitions, including lazy or asynchronously loaded module definitions
  var modules = ({});
  var cache = {};
  // Only the module definitions in modules are read when require
  function require(moduleId){
    if(cache[moduleId]){// See if there are any module objects in the cache
      return cache[moduleId].exports;// If yes, return directly
    }
    Module. exports defaults to an empty object
    var module = {exports: {}}; cache[moduleId]=module;
    Module.exports is assigned a value when the module's code executes
    modules[moduleId].call(module.exports,module.module.exports,require);
    return module.exports;
  }
  require.f={};
  // How to load additional code block chunkId=hello asynchronously
  //2. Create a PROMISE and initiate a JSONP request
  require.e =(chunkId) = >{
    let promises = [];
    require.f.j(chunkId,promises);
    return Promise.all(promises);// After all the promises in the promise array succeed
  }
  require.p=' ';//publicPath Resource access path
  require.u = (chunkId) = >{// The argument is the name of the code block, and the return value is the file name of the code
    return chunkId+'.main.js';
  }
  The name of the code block: 0 indicates that it is ready
  let installedChunks = {
    main:0.hello:0
  }
  //3. Asynchronously load chunkId via jsonp, i.e. the hello code block
  require.f.j = (chunkId,promises) = >{
    // Create a new promise and place it in the array
   let promise = new Promise((resolve,reject) = >{
    installedChunks[chunkId]=[resolve,reject];
   });
   promises.push(promise);
   var url = require.p+require.u(chunkId);// /hello.main.js
   require.l(url);
  }
  / / http://127.0.0.1:8082/hello.main.js
  //4. Request this new URL through JSONP
  require.l = (url) = >{
     let script = document.createElement('script');
     script.src = url;
     document.head.appendChild(script);// Once added to the head, the browser will immediately request it
  }
  //6. Start the callback
  var webpackJsonpCallback = ([chunkIds,moreModules]) = >{
    //chunkIds=['hello']=>[resolve,reject]
    //let resolves = chunkIds.map(chunkId=>installedChunks[chunkId][0]);
    let resolves = [];
    for(let i=0; i<chunkIds.length; i++){let chunkData = installedChunks[chunkIds[i]];
      installedChunks[chunkIds[i]]=0;
      resolves.push(chunkData[0]);
    }
   
    // Merge the extra code blocks asynchronously loaded back into the total module definition object modules
    for(let moduleId in moreModules){
      modules[moduleId]= moreModules[moduleId];
    }
    resolves.forEach(resolve= >resolve());
  }
  require.d = (exports,definition) = >{
    for(let key in definition){
      Object.defineProperty(exports,key,{enumerable:true.get:definition[key]}); }}require.r = (exports) = >{
    Object.defineProperty(exports.Symbol.toStringTag,{value:'Module'});
    Object.defineProperty(exports.'__esModule', {value:true});
  }
  //0. Assign the empty array to window["webpack5"] and overwrite window["webpack5"].push
  var chunkLoadingGlobal = window["webpack5"] = [];// Then override window["webpack5"]. Push =webpackJsonpCallback
  chunkLoadingGlobal.push = webpackJsonpCallback;
  // Asynchronously load the hello block and merge the module definitions in the hello block into the main module definition
  // Load the hello.js module and get the exported result of the module
  //1. Prepare to load asynchronous code block Hello
  require.e("hello").then(require.bind(require."./src/hello.js")).then(result= > {
    console.log(result.default);
  })
})();
Copy the code
// dist/hello.js
//5. Execute the push method on Window ["webpack5"] to pass chunkIds,moreModules.
(window["webpack5"] = window["webpack5"] || []).push([["hello"] and {"./src/hello.js":
        ((module.exports.require) = > {
            require.r(exports);
            require.d(exports, {
                "default": () = > __WEBPACK_DEFAULT_EXPORT__
            });
            const __WEBPACK_DEFAULT_EXPORT__ = ('hello '); }}));Copy the code

Compared to the previously compiled code:

  • require.fobject
  • require.efunction
  • require.pattribute
  • require.u function
  • require.f.jfunction
  • require.lfunction

It also hangs an attribute ‘webpack5’ = [] on the window and overrides the push method on the array.

It’s important to understand the difference between a code block and a module

In hello.js, a push method is used to pass in a two-dimensional array, code block IDS and all modules. The push method is used to call the overridden push method in main.js.

In main.js, require. E (“hello”) is executed first. The main job of e method is to return a Promise array and call j method to implement jSONP asynchronous request code block resource. The essence of JSONP is to create a script tag to complete a remote request. The j method also creates a Promise object for each code block and caches resolve,reject methods. This step is done because the network requests resources asynchronously and it is not certain when the resources will be returned. Call the resolve method to retrieve the module after all resources are returned successfully.

It is important to note that the jSONP method will execute the code after requesting the corresponding code block resource, which also executes the push method mentioned above. Push is the rewritten webpackJsonpCallback method. The webpackJsonpCallback method internally adds the retrieved module to the Modules object, and in turn executes the cached resolve method, which triggers the callback in require. E (“hello”). Then, That’s the require method