- Module loading in Webpack can be synchronous or asynchronous
- The core principle of asynchronous loading is through JSONP
- Webpack module loading is ok
commonjs module
与es module
A mixture of- Import and export in code no matter how it is packaged and compiled, is it
commonjs module
way
- Import and export in code no matter how it is packaged and compiled, is it
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.f
objectrequire.e
functionrequire.p
attributerequire.u
functionrequire.f.j
functionrequire.l
function
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