Webpack is very commonly used in front-end development, but many people’s knowledge of WebPack is only in the understanding stage, and some configuration operations, for The running process of WebPack are not very well understood. I hope this article brings you more knowledge.

In general, webpack packaging only generates JS files called bundles. Bundle files can be very long and can get confused, so developers generally don’t look at the contents of the bundle, just loading it and running it.

So what’s the logic in the bundle?

If you look at the bundle directly, it’s not very clear. You can modify some configurations (mode=development, separate out the manifest file) to make the code clearer

In this configuration, in addition to generating bundle files, there is also a manifest file, which is small and readable because development mode is used.

The manifest file is the entry file for the implementation of the bundle. The following is an interpretation of the logic in the manifest file. Readers can also refer to the code to read and understand it.

As for the code fragment in this paper, the version of Webpack is 4, and the compilation result under the condition of mode: development is configured. // is the original code comment, and //// is the comment written by the author. The comment content includes the comments on the code logic, doubts and thoughts during reading.

The main logic

The entire manifest file is a self-executing function IIFE. Most of the content is the definition of variables and functions.

The code that runs directly in IIFE is as follows

var installedModules = {}
var installedChunks = {
  // The manifest is loaded by default
  "webpackManifest": 0
};

var deferredModules = [];

//// define/get webpackJsonp and bind the push function
var jsonpArray = window["webpackJsonp"] = window["webpackJsonp"] | | [];var oldJsonpFunction = jsonpArray.push.bind(jsonpArray);
//// redefine push
jsonpArray.push = webpackJsonpCallback;
//// Copy array
jsonpArray = jsonpArray.slice();
//// is expected to be the loading logic of the module
for(var i = 0; i < jsonpArray.length; i++) webpackJsonpCallback(jsonpArray[i]);
var parentJsonpFunction = oldJsonpFunction;

// run deferred modules from other chunks
checkDeferredModules();
Copy the code

The code logic is as follows (hereafter called logic A):

  1. Define some variables to get an array called window.webPackJsonp
  2. After saving the old push method of webpackJsonp, use the webpackJsonpCallback function as the new push function
  3. Loop through the contents of webpackJsonp, calling webpackJsonpCallback()
  4. Define parentJsonpFunction
  5. Call the **checkDeferredModules()** function

The main logic of steps 1, 2 and 3 is to define some variables to get webpackJsonp and loop the content for processing, while steps 4 and 5 have no effect here temporarily, or do not know what they do.

Here’s a question: What exactly is in the webpackJsonp array?

WebpackJsonp variable

To understand what’s in webpackJsonp, you need to find out where it’s used. Let’s take a look at the bundle code. Here’s a snippet from the bundle named Main that hides a lot of code details.

(window["webpackJsonp"] = window["webpackJsonp"] || []).push([
  ["main"] and {'/7QA': function(module, __webpack_exports__, __webpack_require__) {
      eval('Module specific code')},'edgnb': function(module, __webpack_exports__, __webpack_require__) {
      eval('Module specific code')},/ /... Other similar code
  },
  [["/7QA"."webpackManifest"."vendors"]]]);Copy the code

As you can see, the webapckJsonp variable is first ensured for system robustness. The next step is to call the push method and pass in an array of mainly three elements (later called triples). I don’t care what these three elements are for. If you look closely, the second element of the triplet stores a lot of module code, business code, or package code referenced or package code in the dependency graph structure.

If you look at the other bundles, the content is similar.

WebpackJsonpCallback function

The push method was called in the previous section, but in step 2 of logic A, the original push method was replaced with the webpackJsonpCallback function, so take A look at the implementation

//// data is a triple
function webpackJsonpCallback(data) {
  //// Take the code in the webpackJsonp section as an example
  //// chunkIds = ["main"]
  var chunkIds = data[0];
  //// moreModules = { "/7QA": function() {} }
  var moreModules = data[1];
  //// executeModules = [["/7QA","webpackManifest","vendors"]]
  var executeModules = data[2];
  
  // 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];//// means if installChunks have chunkId and the corresponding content exists
  	//// but by default, there should be none, so it will be skipped here
  	
  	//// it is also possible that installChunks will later be chunks
  	if(Object.prototype.hasOwnProperty.call(installedChunks, chunkId) && installedChunks[chunkId]) {
  		resolves.push(installedChunks[chunkId][0]);
  	}
  	The //// webpackManifest module will also be marked as 0, but does this conflict with installedChunks[chunkId][0] above, because this is a number and this is an array? Maybe for compatibility with previous logic?
  	installedChunks[chunkId] = 0;
  }
  //// is written to modules
  for(moduleId in moreModules) {
  	if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
  	  //// modules should be an array, but moduleId feels like a strange character, such as /7QA, if this is a problemmodules[moduleId] = moreModules[moduleId]; }}//// call push again
  if(parentJsonpFunction) parentJsonpFunction(data);
  
  //// pop up the element and run
  while(resolves.length) {
  	resolves.shift()();
  }
  
  // add entry modules from loaded chunk to deferred list
  The contents of the last element of the //// triplet are placed in deferredModules
  deferredModules.push.apply(deferredModules, executeModules || []);
  
  // run deferred modules when all chunks ready
  return checkDeferredModules();
}
Copy the code

The code logic is as follows (called logic B later) :

  1. For chunkIds, mark it as 0 in the installedChunks object
  2. For moreModules, store the modules in the Modules variable
  3. Call the parentJsonpFunction function
  4. For deferredModules, place them in executeModules
  5. Call checkDeferredModules

For 1, 2, 4, it’s either going to do some marking, or it’s going to store the data, whatever it is, why it’s stored, what it’s going to do with it.

For step 3, which echoes logic A’s step 4, recall the original push function to add the triple

For step 5, it is the same as step 5 in logic A.

The observant reader will notice that in step 2, a variable named modules appears for the first time, so where does this variable come from? Or where is it defined?

Modules variable

Going back to the beginning and looking at the IIFE code, you’ll see that modules are actually arguments to IIFE. IIFE code is as follows:

//// starts as an empty array
(function(modules) {
  //// principal logic}) ([]);Copy the code

Sometimes you’ll also see code that puts modules directly into modules

(function(modules) {
  //// principal logic({})//// omit details
  "/7QA": function() {},
  "edgnb": function() {}});Copy the code

CheckDeferredModules function

The rest of the ununderstood logic in logic A and B is step 5. The specific code in the function is as follows:

function checkDeferredModules() {
  var result;
  //// Based on the previous code, here deferredModules = [["/7QA","webpackManifest","vendors"]]
  for(var i = 0; i < deferredModules.length; i++) {
    //// deferredModule = ["/7QA","webpackManifest","vendors"]
  	var deferredModule = deferredModules[i];
  	var fulfilled = true;
  	for(var j = 1; j < deferredModule.length; j++) {
  		var depId = deferredModule[j];
  		//// if there are any non-zero cases
  		if(installedChunks[depId] ! = =0) fulfilled = false;
  	}
  	if(fulfilled) {
  	  //// no longer check
  		deferredModules.splice(i--, 1);
  		//// __webpack_require__(__webpack_require__.s = '/7QA')
  		result = __webpack_require__(__webpack_require__.s = deferredModule[0]); }}return result;
}
Copy the code

The code logic is as follows:

  1. Check that the array of the third element in the triplet, starting with subscript 1, is marked as 0 in installChunks.
  2. If the first result is true, the **__webpack_require__ function ** is called with the module subscript 0, and the result is returned.

In the previous code, in the case of both chunks, webpackManifest and vendors, being marked, __webpack_require__(__webpack_require__.s = ‘/7QA’) is called.

__webpack_require__ function

This function is defined in IIFE as follows

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

The code logic is as follows:

  1. If installModules already has moduleId content, return it directly.
  2. Otherwise, if there is no content, add content to installModules, call the corresponding module with the call function, and return the result.

At this point, the business module is already running.

conclusion

Read the MANIFEST code to see some of the running logic of the WebPack bundle code, but there is a lot of other code not listed in IIFE that you can read for yourself.