Loading of webpack

Time:2022-1-7

Webpack is commonly used in front-end development, but many people only stay in the understanding stage of webpack and some configuration operations, and don’t know more about the running process of webpack. I hope this article will bring you more knowledge.

In general, webpack packaging only generates JS files called bundles. The bundle file may be very long and may be confused, so most developers don’t look at the specific contents of the file, but only know that the file can be loaded and run.

So what is the logic in the bundle file?

Looking directly at the bundle is not very clear. You can modify some configurations (mode = development, separate the manifest file) to make the code clearer

In this configuration, in addition to generating some bundle files, there is also a manifest file. The file is relatively small, and because the development mode is used, the manifest file can be read.

The manifest file is the entry file for the bundle to run. The following is the interpretation of the logic in the manifest. Readers can also refer to the code to read and understand it by themselves. It is recommended to sort out the code by themselves.

For the code snippets in this article, the webpack version is 4, which is configured asmode: developmentCompilation results in case,//It’s a comment on the original code,////It is a comment written by the author. The content of the comment includes comments on code logic, doubts and thoughts in reading.

Subject logic

The entire manifest file is a self executing functionIIFE。 Most of the content is the definition of variables and functions.

The code for direct operation in Iife is as follows

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

var deferredModules = [];

////Define / obtain webpackjsonp and bind push function
var jsonpArray = window["webpackJsonp"] = window["webpackJsonp"] || [];
var oldJsonpFunction = jsonpArray.push.bind(jsonpArray);
////Redefine push
jsonpArray.push = webpackJsonpCallback;
////Copy array
jsonpArray = jsonpArray.slice();
////Expected module loading logic
for(var i = 0; i < jsonpArray.length; i++) webpackJsonpCallback(jsonpArray[i]);
var parentJsonpFunction = oldJsonpFunction;

// run deferred modules from other chunks
checkDeferredModules();

The code logic is as follows (hereinafter referred to asLogic a):

  1. Define some variables and get the name window Array of 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 and callwebpackJsonpCallback()
  4. definitionparentJsonpFunction
  5. callcheckDeferredModules()function

The main logic of steps 1, 2 and 3 is to define some variables to obtain webpackjsonp and process the loop content. Steps 4 and 5 are temporarily ineffective or do not know what to do.

When you see this, you will have a question: what is in the webpackjsonp array?

Webpackjsonp variable

In order to understand the content in webpackjsonp, you need to find out where it is used.
Let’s take a look at the bundle code. The following is the code fragment in the bundle named main, which hides a lot of code details.

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

It can be seen that for the sake of system robustness, first ensure the existence of webapckjsonp variable. The second is to call the push method and pass in an array, which mainly contains three elements (later calledTriplet)。 What are these three elements used for? Let’s leave it alone.
If you look closely,TripletThe second element stores a large number of module code, that is, business code, or referenced package code, or package code in the dependency graph structure.

Look at other bundle files. The contents are similar.

Webpackjsonpcallback function

The push method was invoked in the last section, but in the second step of logical A, the webpackJsonpCallback function was used to replace the original push method.

////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];
      ////It is understood that if the chunkid exists in installchunks and the corresponding content exists
      ////But by default, there should be none, so it will be skipped here
      
      ////Maybe installchunks will be chunks later
      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 the installedchunks [chunkid] [0] above, because here is a number and above is an array? Maybe to be compatible with previous logic?
      installedChunks[chunkId] = 0;
  }
  ////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. Is there a problem with this
          modules[moduleId] = moreModules[moduleId];
      }
  }
  ////Call the push function 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();
}

The code logic is as follows (hereinafter referred to asLogic B):

  1. For chunkids, mark it as 0 in the installedchunks object
  2. For moremodules, store the modules inmodulesVariable
  3. Call the parentjsonpfunction function
  4. For deferredmodules, put it in executemodules
  5. Call the checkdeferredmodules function

For 1, 2 and 4, you can either mark them or store data. Why do you want to save them and how to use them for the time being.

For step 3, andLogic a step 4Echo, that is, call the original push function againTripletjoin

For step 5, andLogic a step 5Same.

Careful readers may find that in step 2, the namemodulesSo where does this variable come from? Or where is it defined?

Modules variable

If you go back to the beginning and take a closer look at the Iife code, you will find that modules are actually Iife parameters. Iife codes are as follows:

////Initially, it is an empty array
(function(modules) {
  ////Subject logic
})([]);

Sometimes you can see such code and directly put the module intomodulesin

(function(modules) {
  ////Subject logic
})({
  ////Omit details
  "/7QA": function() {},
  "edgnb": function() {},
});

Checkdeferredmodules function

Logic aandLogic BThe remaining logic not understood in the function is step 5. The specific code in the function is as follows:

function checkDeferredModules() {
  var result;
  ////According to the previous code, 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 it exists, it is not 0
          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;
}

The code logic is as follows:

  1. inspectTripletIn the array of the third element in, whether it is marked as 0 in installchunks starting from subscript 1.
  2. If the result of the first is true, call__ webpack_ require__ function, the parameter is the module corresponding to the subscript 0, and finally returns the result.

In the previous code, i.ewebpackManifestandvendorsWhen both chunks are marked, the__webpack_require__(__webpack_require__.s = '/7QA')

__ webpack_ require__ function

This function is also defined in Iife. The specific code is 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;
}

The code logic is as follows:

  1. If installmodules already has the corresponding content of moduleid, it will be returned directly.
  2. On the contrary, if there is no content, first add content to installmodules, call the corresponding module through the call function, and finally return the result.

At this point, the business module is already running.

summary

Through reading the manifest code, you can understand some of the running logic of the webpack bundle code. In addition, there are many other codes not listed in Iife, which readers can read by themselves.


Some other articles on webpack:

  1. Chunk generation logic of webpack

Recommended Today

Springboot 2.6.3 integrated redis stepped on the pit

The integration steps are as follows: development tools: idea2019, JDK1.8, maven 3.5.4 Idea creates a new project, selects spring initializer, selects spring boot version 2.6.3 (the latest version at present), and adds web, and redis modules. After successful construction, the POM file is as follows: <?xml version=”1.0″ encoding=”UTF-8″?> <project xmlns=”http://maven.apache.org/POM/4.0.0″ xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance” xsi:schemaLocation=”http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd”> <modelVersion>4.0.0</modelVersion> <parent> […]