Start by executing the function immediately

/ SRC /index.js. Webpack uses the relative resource address as the module’s key.

  (function(modules) ({{})"./src/index.js":
    (function(module.exports, __webpack_require__) {
    const page = () = > {
     return __webpack_require__.e(/ *! import() */ 0).then(__webpack_require__.t.bind(null."./src/page.js".7)}})})Copy the code

The code first executes the immediate-execute function, passing in modules as a key-value pair. Each module’s loading function takes three arguments:

  • Module is webpack’s abstraction of module. Its data structure includes module ID (I), whether the module is loaded (L) and exported objects of the module. Everything is a module in Webpack.
  • Exports, for module exports.
  • __webpack__require__ loads functions for modules, either asynchronously or synchronously.

__webpack_require__

// 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;
/ * * * * * * / 	}
Copy the code

This function takes a moduleId, the moduleId argument.

  • Check if the module is in the installedModules cache
  • If there is no module corresponding to the moduleId, create a module. Assign the wrapper objects of this module tomoduleVariables andinstalledModules[moduleId], so there is a cache of the module ID in the cache.
  • I is the id of this module,
  • L indicates whether the module is loaded
  • Exports stands for this module

Modules [‘./ SRC /index.js’] is executed with the context of module.exports, where the first argument is module(the wrapper object for the module), the second argument is the actual module, and the third argument is the __webpack_require__ function itself. Since page.js is an asynchronous module, the __webpack_require__ asynchronous module loader is called.

Asynchronous module loadingrequireEnsure

/ * * * * * * / 	// object to store loaded and loading chunks
/ * * * * * * / 	// undefined = chunk not loaded, null = chunk preloaded/prefetched
/ * * * * * * / 	// Promise = chunk loading, 0 = chunk loaded
/ * * * * * * / 	var installedChunks = {
/ * * * * * * / 		"main": 0
/ * * * * * * / 	};
Copy the code

By default, the main.js module is loaded in the completed state. There are four processes for asynchronous module loading:

  • Undefined indicates that the asynchronous module is not loaded
  • Null indicates that the asynchronous module is preloaded
  • Promise indicates that the module is loading
  • 0 indicates that the module is loaded successfully

In the Promise state, we can display a mode-loaded animation where the module renders. The mechanism for loading webPack modules is still unclear, so let’s move on.

chunkId

Each asynchronous module has a chunkId. Such as the. / SRC/page. Js. We load the page.js content asynchronously through the __webpack_require__ asynchronous module loading function.

(window["webpackJsonp"] = window["webpackJsonp"] || []).push([[0] and {"./src/page.js":
 (function(module.exports) {
function component() {
    const element = document.createElement('div');
    element.innerHTML =['Hello'.'webpack'].join(', ')
  
    return element;
  }
  document.body.appendChild(component()); }}));Copy the code

Looking at the contents of this asynchronous module, webpackJsonp is a global variable. It is an array, and the elements of the array are arrays (represented by moduleX). The first element of moduleX is an array, and the second element is a module list, which stores each module. I’ll leave it to the following code to see why. WebpackJsonp maintains a list of modules.

// This file contains only the entry chunk.
/ * * * * * * / 	// The chunk loading function for additional chunks
/ * * * * * * / 	__webpack_require__.e = function requireEnsure(chunkId) {
/ * * * * * * / 		var promises = [];
/ * * * * * * /
/ * * * * * * /
/ * * * * * * / 		// JSONP chunk loading for javascript
/ * * * * * * /
/ * * * * * * / 		var installedChunkData = installedChunks[chunkId];
/ * * * * * * / 		if(installedChunkData ! = =0) { // 0 means "already installed".
/ * * * * * * /
/ * * * * * * / 			// a Promise means "currently loading".
/ * * * * * * / 			if(installedChunkData) {
/ * * * * * * / 				promises.push(installedChunkData[2]);
/ * * * * * * / 			} 
/ * * * * * * / 		}
/ * * * * * * / 		return Promise.all(promises);
/ * * * * * * / 	};
Copy the code
  • This function uses the Promise asynchronous API
  • InstalledChunkData indicates the loading status of the asynchronous module. If the loading status is not equal to 0, the module loading process is performed. Process according to the four states described above.
  • 0, null,undefined are falsy values, so onlyPromiseBefore enteringif(installedChunkData) Branch.

Create a process to load JS asynchronous Promises

else {
/ * * * * * * / 				// setup Promise in chunk cache
/ * * * * * * / 				var promise = new Promise(function(resolve, reject) {
/ * * * * * * / 					installedChunkData = installedChunks[chunkId] = [resolve, reject];
/ * * * * * * / 				});
Copy the code

You start by creating a Promise, which immediately changes the value of the installedChunkData state to a Promise state and changes the value of the cache module state to an array of resolve, Reject. Here’s the question, why not just assign the promise directly to installedChunkData? Moving on to the code:

promises.push(installedChunkData[2] = promise);
Copy the code

Here you change the element with index 2 of the installed module directly to a promise. There’s a bigger question mark.

script.onerror = script.onload = onScriptComplete;
Copy the code

Handle script loading errors or success uniformly.

	var timeout = setTimeout(function(){
/ * * * * * * / 					onScriptComplete({ type: 'timeout'.target: script });
/ * * * * * * / 				}, 120000);
Copy the code

If onScriptComplete is not called, an error will be emitted at a future time. The timeout period is 2 minutes. In other words, if an asynchronous module is not loaded within 2 minutes, the module is considered to have failed to be loaded. So let’s look at onScriptComplete.

onScriptComplete = function (event) {
/ * * * * * * / 					// avoid mem leaks in IE.
/ * * * * * * / 					script.onerror = script.onload = null;
/ * * * * * * / 					clearTimeout(timeout);
/ * * * * * * / 					var chunk = installedChunks[chunkId];
/ * * * * * * / 					if(chunk ! = =0) {
/ * * * * * * / 						if(chunk) {
/ * * * * * * / 							var errorType = event && (event.type === 'load' ? 'missing' : event.type);
/ * * * * * * / 							var realSrc = event && event.target && event.target.src;
/ * * * * * * / 							error.message = 'Loading chunk ' + chunkId + ' failed.\n(' + errorType + ':' + realSrc + ') ';
/ * * * * * * / 							error.name = 'ChunkLoadError';
/ * * * * * * / 							error.type = errorType;
/ * * * * * * / 							error.request = realSrc;
/ * * * * * * / 							chunk[1](error);
/ * * * * * * / 						}
/ * * * * * * / 						installedChunks[chunkId] = undefined;
/ * * * * * * / 					}
/ * * * * * * / 				};
Copy the code

First, clear onError and onLoad to avoid MEMORY leakage of IE, and clear the timer. If chunk is not loaded successfully, the asynchronous module is not loaded successfully. A ChunkLoadError is thrown. This error is typically 404 for JS and CSS. The question is, what state is installedChunks = 0?

	// script path function
/ * * * * * * / 	function jsonpScriptSrc(chunkId) {
/ * * * * * * / 		return __webpack_require__.p + "" + ({}[chunkId]||chunkId) + ".js"
/ * * * * * * / 	}
Copy the code

The jsonScriptSrc function is used to get the chunk’s path. Just get the path. After the asynchronous module is loaded, such as page.js. Is called webpackJsonpCallback. Why? Because the push method of the webpackJson array is hijacked by webpackJsonpCallback. The webpackJsonp variable will perform more operations as it is pushed into more modules.

/ * * * * * * / 	var jsonpArray = window["webpackJsonp"] = window["webpackJsonp"] | | [];/ * * * * * * / 	var oldJsonpFunction = jsonpArray.push.bind(jsonpArray);
/ * * * * * * / 	jsonpArray.push = webpackJsonpCallback;
/ * * * * * * / 	jsonpArray = jsonpArray.slice();
/ * * * * * * / 	for(var i = 0; i < jsonpArray.length; i++) webpackJsonpCallback(jsonpArray[i]);
/ * * * * * * / 	var parentJsonpFunction = oldJsonpFunction;
Copy the code
/ * * * * * * / 	var jsonpArray = window["webpackJsonp"] = window["webpackJsonp"] | | [];/ * * * * * * / 	var oldJsonpFunction = jsonpArray.push.bind(jsonpArray);
/ * * * * * * / 	jsonpArray.push = webpackJsonpCallback;
/ * * * * * * / 	jsonpArray = jsonpArray.slice();
/ * * * * * * / 	for(var i = 0; i < jsonpArray.length; i++) webpackJsonpCallback(jsonpArray[i]);
/ * * * * * * / 	var parentJsonpFunction = oldJsonpFunction;
/ * * * * * * /
/ * * * * * * /
/ * * * * * * / 	// Load entry module and return exports
/ * * * * * * / 	return __webpack_require__(__webpack_require__.s = "./src/index.js");
Copy the code

We define a jsonpArray data and assign the value to the Window’s webpackJsonp property. Change the array’s push method to webpackJsonpCallback to perform more operations on the push module. JsonpArry [I] returns an array, key. This is why the data structure of the asynchronous module push is an array, the first element of which is an array, and the second element is the module table. Take a look at the following code to remember:

(window["webpackJsonp"] = window["webpackJsonp"] || []).push([[0] and {"./src/page.js":
 (function(module.exports) {
function component() {
    const element = document.createElement('div');
    element.innerHTML =['Hello'.'webpack'].join(', ')
  
    return element;
  }
  document.body.appendChild(component()); }}));Copy the code

It is this data structure that webpackJsonpCallback needs to use. First, the first element of the array represents chunkIds. The second argument is an object representing more modules. Then change the loading state of all chunkId to 0, loaded state. Place the parsed module into the parse array. And execute them one by one. Here we need to understand what a few variables are:

  • InstalledChunks, as mentioned earlier, save module loading status.
  • Convergent, execution function of each module
  • Modules, used to store all modules.
  • ParentJsonpFunction, the array’s push method, pushes the module into the webpackJsonp array variable.

Reading this, we may still be vague about the entire workflow. However, the previous reading is in preparation for later breakpoint debugging.

/ * * * * * * / 	function webpackJsonpCallback(data) {
/ * * * * * * / 		var chunkIds = data[0];
/ * * * * * * / 		var moreModules = data[1];
/ * * * * * * /
/ * * * * * * /
/ * * * * * * / 		// add "moreModules" to the modules object,
/ * * * * * * / 		// then flag all "chunkIds" as loaded and fire callback
/ * * * * * * / 		var moduleId, chunkId, i = 0, resolves = [];
/ * * * * * * / 		for(; i < chunkIds.length; i++) {/ * * * * * * / 			chunkId = chunkIds[i];
/ * * * * * * / 			if(installedChunks[chunkId]) {
/ * * * * * * / 				resolves.push(installedChunks[chunkId][0]);
/ * * * * * * / 			}
/ * * * * * * / 			installedChunks[chunkId] = 0;
/ * * * * * * / 		}
/ * * * * * * / 		for(moduleId in moreModules) {
/ * * * * * * / 			if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
/ * * * * * * / 				modules[moduleId] = moreModules[moduleId];
/ * * * * * * / 			}
/ * * * * * * / 		}
/ * * * * * * / 		if(parentJsonpFunction) parentJsonpFunction(data);
/ * * * * * * /
/ * * * * * * / 		while(resolves.length) {
/ * * * * * * / 			resolves.shift()();
/ * * * * * * / 		}
/ * * * * * * /
/ * * * * * * / 	};
Copy the code

Breakpoint interpretation

The best way to really understand the source code is to run it at breakpoints.

As we can see, the main entry to the code immediately executes the function passed in a modules variable. The module list has only one registration value, ‘./ SRC /index.js’, which is the entry to the package of the project. This is a total module. It then initializes the normal and function variables and executes directly to the return statement.

return __webpack_require__(__webpack_require__.s = "./src/index.js");
Copy the code

Here the __webpack_require__ function passes in a moduleId,’./ SRC /index.js’. This installedModules is also an empty object, which gets you into the process of creating a wrapper module. Execute the loading function of the entry module. This is the code execution of index.js. When we need to request an asynchronous module page.js, we create a script tag to load 0.js, where 0 is the chunkId of the asynchronous block. So we get to webpackJsonpCallback, which hijacks the asynchronous module into the Modules variable. When the CONVERgent execution occurs, the THEN operation of the Promise is executed.

 __webpack_require__.t.bind(null./ *! ./page */ "./src/page.js".7)
Copy the code

This operation executes the contents of the asynchronous module. Thus, the contents of the asynchronous module are displayed on the page. Thanks for reading.