I’ve been learning about WebPack recently, and I’ve come to understand that WebPack is a combination of different loaders and plugins that can be used only as a tool. Of course, encountering countless pits along the way has led to a desire to dig a little deeper into webpack’s principles (mainly the fact that you can climb out of a pit on your own). So let’s start with a simple look at how to load a JS file packaged with WebPack.

As a bonus, this article is easy to follow, even if you haven’t used WebPack before. If you already know the relevant knowledge of friends, might as well read quickly, is to reexamine the old and new, in fact is to ask you to tell me where to write wrong.

Simple configuration

Webpack: webpack: webpack: webpack: webpack: webpack: webpack: webpack: webpack: webpack: webpack: webpack: webpack: webpack: webpack: webpack: webpack: webpack: webpack: webpack: webpack: webpack: webpack.

const path = require('path'); const webpack = require('webpack'); Const HtmlWebpackPlugin = require('html-webpack-plugin'); Const CleanWebpackPlugin = require('clean-webpack-plugin'); module.exports = { entry: { index: path.join(__dirname, 'index.js'), }, output: { path: path.join(__dirname, '/dist'), filename: 'js/[name].[chunkhash:4].js' }, module: {}, plugins: [ new CleanWebpackPlugin(['dist']), new HtmlWebpackPlugin({ filename: 'index.html', template: 'index.html',}), // persisting moduleId, Mainly to study after loading code prettier. New webpack. HashedModuleIdsPlugin (), the new webpack.optimize.Com monsChunkPlugin ({name: 'manifest', }) ] };Copy the code

This is about as simple a configuration as I can think of, and the two additional plug-ins that are downloaded are both very common and have been briefly explained in the comments.

This is followed by two simple JS files:

// test.js
const str = 'test is loaded';
module.exports = str;

// index.js
const test = require('./src/js/test');
console.log(test);
Copy the code

The directory structure of the project should look like this:

At this point, our configuration is complete.

fromindex.jsStart looking at the code

First look at the loading order of the two JS files from the packaged index.html file:

<body>
	<script type="text/javascript" src="js/manifest.2730.js"></script>
	<script type="text/javascript" src="js/index.5f4f.js"></script>
</body>
Copy the code

As you can see, the load order of the packaged JS file is manifest.js first, followed by index.js, so you should look at the contents of manifest.js first. However, let’s take a look at the contents of index.js first, so that we can understand how manifest.js, the logic of the main process, works and why it can be modularized.

// index.js

webpackJsonp([0], {
  "JkW7": (function(module, exports, __webpack_require__) {
    const test = __webpack_require__("zFrx");
    console.log(test);
  }),
  "zFrx": (function(module, exports) {
    const str = 'test is loaded';
    module.exports = str;
  })
}, ["JkW7"]);
Copy the code

The webpackJsonp function is not in any namespace. Manifest. Js should define a global function that is hung in the window. Index. js passes three arguments to this function and calls it.

The first argument is an array, and it’s not clear what the array does.

The second argument is an object filled with methods that appear to take at least two parameters (the method named zFrx has only two parameters). Take a look inside the two methods and you actually see something very familiar, module.exports, which has a similar-looking __webpack_require__ even though you don’t see require. These two functions should be key to modularity, so keep these two functions in mind.

The third argument is also an array, and it’s not clear what it does, but we observe that its value is JkW7, which is consistent with the key of one of the methods in argument 2, so there may be some logical correlation.

At this point, the contents of index.js are gone, and you should look for answers in manifest.js with questions.

manifest.jsCode reading

Since no option to compress JS is configured, the source code for manifest.js is around 150 lines, which is simplified to 28 lines. In view of the simplified code is really not much, so let’s post the code first, with the question just raised, let’s see how many answers we can find:

(function(modules) {
  window["webpackJsonp"] = function webpackJsonpCallback(chunkIds, moreModules, executeModules) {
    var moduleId, result;
    for (moduleId in moreModules) {
      if (Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
        modules[moduleId] = moreModules[moduleId];
      }
    }
    if (executeModules) {
      for (i = 0; i < executeModules.length; i++) {
        result = __webpack_require__(executeModules[i]);
      }
    }
    return result;
  };
  var installedModules = {};

  function __webpack_require__(moduleId) {
    if (installedModules[moduleId]) {
      return installedModules[moduleId].exports;
    }
    var module = installedModules[moduleId] = {
      exports: {}
    };
    modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
    return module.exports;
  }
})([]);
Copy the code

Manifest.js is a self-executing IIFE function that takes an empty array as an argument named Modules. Then we saw what we suspected in index.js, and sure enough there was a function called webpackJsonp hanging in the window. It accepts three parameters named chunkIds, moreModules, and executeModules. This corresponds to the three parameters passed in the call to webpackJsonp in index.js. What kind of logic does webpackJsonp have?

Regardless of the parameters defined, webpackJsonp first iterates through moreModules for in, leaving all methods in moreModules in modules, which are arrays passed in when the self-executing function executes.

Then there is a conditional judgment:

if (executeModules) { for (i = 0; i < executeModules.length; i++) { result = __webpack_require__(executeModules[i]); }}Copy the code

Determine whether executeModules, the third parameter, exists, and if so, execute the __webpack_require__ method. This parameter is of course present when index.js calls the webpackJsonp method, so it’s time to see what the __webpack_require__ method is.

__webpack_require__ takes a parameter named moduleId. The inside of the method is first of all a condition judgment, but let’s forget it. Next you see the assignment logic

var module = installedModules[moduleId] = {
  exports: {}
};
Copy the code

If there is a moduleId in the cache, then return its exports directly, or define and assign one to it. If you look at the final return value of __webpack_require__, you can see that the function returns module.exports. How is module.exports assigned? Take a look at the following code:

modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
Copy the code

Modules [moduleId] = modules[moduleId] = module. Module, module.exports, and __webpack_require__ are passed in as parameters. Are these three parameters familiar? When we looked at the code in index.js earlier, one question was how modularity was implemented. We’ve already seen the Outlines here.

Webpack encapsulates each JS file into a function. The require method in each file corresponds to __webpack_require__. __webpack_require__ loads the code based on the passed moduleId. Exports. This corresponds to the module and module.exports parameters. If you don’t touch this piece of shoes, you can understand why exports failed when you directly used exports = XXX. Here’s a quick example:

const module = { exports: {} }; function demo1(module) { module.exports = 1; } demo1(module); console.log(module.exports); // 1 function demo2(exports) { exports = 2; } demo2(module.exports); console.log(module.exports); / / 1Copy the code

Paste this code and run it through the browser, and you’ll see that it prints out 1 twice. This is exactly the same as wenpack packaging logic.

To sort out the process of executing the packaged code, first minifest.js will define a webpackJsonp method, which will be called by other packaged files (also known as chunks). When calling a chunk, all the moreModules of the chunk, that is, each dependent file (also known as a module, such as test.js), are stored first. ExecuteModules then determine whether this file is the entry file and whether __webpack_require__ should be executed for the first time. __webpack_require__ recursively calls __webpack_require__ based on what the module requires. __webpack_require__ returns a value that will be used by require. Of course, modules are not loaded repeatedly, because installedModules records the value of exports after a module call. Once the cache is hit, it returns the corresponding value without calling the Module again. Webpack files are packaged to isolate module scopes through functions so as not to contaminate each other.

summary

The above is a simple description of the loading process of WebPack js file after packaging. In fact, there are still many details not finished, such as how to load the corresponding module asynchronously, what is the role of chunkId, etc., but due to the limited space (haven’t researched thoroughly), no longer detailed. The relevant code will be placed on Github, please feel free to check it by clicking star.

Thank you for seeing here, it is easier to know than to do, I hope this article will help you ~ thank you!