preface

Postscript: Since webpack's knowledge base is too large, and Webpack is a very deep framework, so let's break it down. This time, we will talk about the basic principles and history of packaging, and then we will introduce it as deeply as possible: Tapable (5) tree-shaking (6) sourceMap (7) HMR

Front-end packaging, build, gulp, grunt, webpack, rollup, etc. It can be said that with the advent of Node, the front-end has become more and more complex, because JS code is no longer a weak language that only runs in the browser, and with it comes the ability to run on the server as well. I think the biggest benefit is that front-end projects can also be “engineered”, just like C, with pre-processing, compilation, assembly, linking capabilities. Of course javascript is an interpreted language, so there are no last three steps, and front-end packaging is more or less a preprocessing + modularization process.

Understanding front-end modularity

Why modularity: Is it all in main.js? How to reuse? How to collaborate on development? But JS is not like the others

scope

Global scope, local scope global: window, globalCopy the code
<! DOCTYPEhtml>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
  <title>Document</title>
</head>
<body>
  <script src="./moduleA.js"></script>
  <script src="./moduleB.js"></script>
  <script src="./moduleC.js"></script>
</body>
</html>
Copy the code
// moduleA.js
var a = 1;
Copy the code
// moduleB.js
var a = 2;
Copy the code
// moduleC.js
var b = a + 1;
console.log('b = ', b);
Copy the code
Result :b = 3Copy the code

It’s obviously covered. What to do?

The namespace

//moduleA
var a = {
  value: 1};Copy the code
//moduleB
var b = {
  value: 1};Copy the code
//moduleC
console.log('moduleA的value', a.value);
console.log('moduleB的value', b.value);
Copy the code
Result: Value of moduleA1The value of moduleB2
Copy the code

It seemed to solve the above problem, but then came the problem:

a.value = 100;
Copy the code

It’s very easy to change one of the variables inside so we need to tweak it with scopes and closures

  var moduleA = (function() {
    var name = 'Nolan';

    return {
      myNameIs: function() {
        console.log('Please call me', name); }}; }) ();Copy the code

This is an immediate function.

moduleA.myNameIs();
// Please call me Nolan

moduleA.name;
// undefined
Copy the code

It’s clearly exposing what needs to be exposed and hiding what needs to be hidden. So let’s optimize the way we write it

(function(window) {
  var name = 'Nolan';
  function myNameIs() {
    console.log('Please call me', name);
  }
  
  window.moduleA = { myNameIs }; }) (window)
Copy the code

If you flick through the webpack packed code and compare it, doesn’t it look very similar?

To summarize

So we see a technology is step by step out, think about a handwritten JS inheritance is not also step by step to solve the problem, encounter new problems, and then solve the problem, the final result. Advantages:

  • Scope encapsulation
  • reuse
  • Remove the coupling

modular

History

  • AMD
  • COMMONJS
  • ES6 MODULE

AMD

define('moduleName'['lodash'].function(_) {
  return function(a, b) {
    console.log(a, b);
  };
});
Copy the code

For example, requireJS later spawned the famous work Sea-.js (CMD).

COMMONJS

It was introduced in 2009 to standardize server-side development, not for browsers. So Nodejs later referenced this standard as well.

const moduleA = require('./moduleA');

exports.getSum = function(a, b) {
  console.log(a + b);
}
Copy the code

As with AMD, there is an emphasis on introduced modules.

ES6 MODULE

Is very similar to COMMONS

import moduleA from './moduleA';

export function getName(name) {
  return name;
}

Copy the code

Gulp and Grunt are automated build tools. Automation is emphasized here because they can do more than just packaging. Automation is their core purpose. The emergence of Webpack can be said to focus on packaging.

webpack

Let’s start with a little example

HTML, SRC /index.js, and SRC /util.js. NPM installs Webpack and the WebPack-CLI tool currently uses version 4.x.x. Tree-shake performance is optimized for V5, so the results of the build will be different. We’ll talk about what a tree-shake is later. index.html

<! DOCTYPEhtml>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
  <title>My Webpack Test</title>
</head>
<body>
  <script src="./dist/main.js"></script>
</body>
</html>
Copy the code

index.js

const num = require('./util');

function test() {
  console.log('I'm a little test! ', num);
}

test();
Copy the code

util.js

exports.default = 123;
Copy the code

NPX webpack or./node_module/.bin/webpack at the root will generate dist/main.js

! function(e) {
    var t = {};

    function n(r) {
        if (t[r]) return t[r].exports;
        var o = t[r] = {
            i: r,
            l:!1.exports: {}};return e[r].call(o.exports, o, o.exports, n), o.l = !0, o.exports
    }
    n.m = e, n.c = t, n.d = function(e, t, r) {
        n.o(e, t) || Object.defineProperty(e, t, {
            enumerable:!0.get: r
        })
    }, n.r = function(e) {
        "undefined"! =typeof Symbol && Symbol.toStringTag && Object.defineProperty(e, Symbol.toStringTag, {
            value: "Module"
        }), Object.defineProperty(e, "__esModule", {
            value:!0
        })
    }, n.t = function(e, t) {
        if (1 & t && (e = n(e)), 8 & t) return e;
        if (4 & t && "object"= =typeof e && e && e.__esModule) return e;
        var r = Object.create(null);
        if (n.r(r), Object.defineProperty(r, "default", {
                enumerable:!0.value: e
            }), 2 & t && "string"! =typeof e)
            for (var o in e) n.d(r, o, function(t) {
                return e[t]
            }.bind(null, o));
        return r
    }, n.n = function(e) {
        var t = e && e.__esModule ? function() {
            return e.default
        } : function() {
            return e
        };
        return n.d(t, "a", t), t
    }, n.o = function(e, t) {
        return Object.prototype.hasOwnProperty.call(e, t)
    }, n.p = "", n(n.s = 0)
}([function(e, t, n) {
    const r = n(1);
    console.log("I'm a little test!", r)
}, function(e, t) {
    t.default = 123
}]);

Copy the code

Let’s not worry about that big mess

Function (module){})([index.js, util.js])

Look at the structure and see if it is an immediate function! So, the great WebPack is simply implemented with the just-in-time functions mentioned earlier. What about all that code that wasn’t written by a human? Why does it look like a bird? Let’s first make the code readable.

Webpack –help shows the –mode parameter, development and production values. The default is production, and we can also see output like this when we run NPX webpack:

NPX webpack –mode=development

(function(modules) { // webpackBootstrap
  // Define a cache
  var installedModules = {};
  // Call it webpack's require method running on the browser. The argument is the key in the argument to the function that executes immediately
  function __webpack_require__(moduleId) {
    If there is a cache, return the data in the cache
    if(installedModules[moduleId]) {
      return installedModules[moduleId].exports;
    }
    // Happily put it in cache. Note that the exports object is defined here
    var module = installedModules[moduleId] = {
      i: moduleId,
      l: false.exports: {}};// Execute the function
    / / think about, we in the IDE crazy mindless write import/require/exports/export these modules
    // When running in the browser, the browser doesn't know it's dry, but what does the browser know?
    Module. exports = module. Exports = module. Exports = module.exports = module
    __webpack_require__ = __webpack_require__
    // The require() argument is the module's path, i.e. the key in the immediate function argument
    // Exports is an object
    modules[moduleId].call(module.exports, module.module.exports, __webpack_require__);
    / / is not important
    module.l = true;
    // require(a file path), this file exports stuff
    return module.exports;
  }
  // Add a dependency module to the built-in require object
  __webpack_require__.m = modules;
  // Add a cache for the built-in require object
  __webpack_require__.c = installedModules;

  // exports object adds a getter
  __webpack_require__.d = function(exports, name, getter) {
    if(! __webpack_require__.o(exports, name)) {
      Object.defineProperty(exports, name, { enumerable: true.get: getter }); }};// In the following example
  __webpack_require__.r = function(exports) {
    if(typeof Symbol! = ='undefined' && Symbol.toStringTag) {
      / / Object. The prototype. ToString. Call (exports) returns the Module
      // The feeling is to look good
      Object.defineProperty(exports.Symbol.toStringTag, { value: 'Module' });
    }
    Object.defineProperty(exports.'__esModule', { value: true });
  };
  // To be honest, I'm not quite sure what this method does
  ESM CJS interop. import("commonjs") will use it.
  / / address paste below: https://github.com/webpack/webpack/issues/11024
  __webpack_require__.t = function(value, mode) {
    if(mode & 1) value = __webpack_require__(value);
    if(mode & 8) return value;
    if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
    var ns = Object.create(null);
    __webpack_require__.r(ns);
    Object.defineProperty(ns, 'default', { enumerable: true.value: value });
    if(mode & 2 && typeofvalue ! ='string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
    return ns;
  };
  // In the following example
  __webpack_require__.n = function(module) {
    var getter = module && module.__esModule ?
      function getDefault() { return module['default']; } :
      function getModuleExports() { return module; };
    __webpack_require__.d(getter, 'a', getter);
    return getter;
  };
  // Contains attributes No
  __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
  / / is not important
  __webpack_require__.p = "";
  // require entry file
  return __webpack_require__(__webpack_require__.s = "./src/index.js"); ({})// The entry method
  "./src/index.js": (function(module.exports, __webpack_require__) {
    eval("const num = __webpack_require__(/*! ./util */ \"./src/util.js\"); \n\nfunction test() {\n console.log(' I am a small test! ', num); \n}\n\ntest(); \n\n\n//# sourceURL=webpack:///./src/index.js?");
  }),
  // The introduced method
  "./src/util.js": (function(module.exports) {
    eval("exports.default = 123; \n\n//# sourceURL=webpack:///./src/util.js?"); })});Copy the code

It’s a little easier to read this time. I’ll write the function’s function in the comment.

So let’s try another ES6 MODULE with CJS and modify index.js

import { num } from './util';

function test() {
  console.log('I'm a little test! ', num);
}

test();
Copy the code

And util. Js

exports.num = 123;
Copy the code

Let’s take a look at the results again. We won’t talk about the repetitions, focusing on the differences between __webpack_require__. N and __webpack_require__

(function (modules) { // webpackBootstrap
  var installedModules = {};
  function __webpack_require__(moduleId) {
    if (installedModules[moduleId]) {
      return installedModules[moduleId].exports;
    }
    var module = installedModules[moduleId] = {
      i: moduleId,
      l: false.exports: {}}; modules[moduleId].call(module.exports, module.module.exports, __webpack_require__);
    module.l = true;
    return module.exports;
  }
  __webpack_require__.m = modules;
  __webpack_require__.c = installedModules;
  // 20. When you call exports.a, use the getter that you passed in
  If you use import X from './util' in index.js;
  // 22.console.log(X) is the getter for.a. Call it a day.
  __webpack_require__.d = function (exports, name, getter) {
    if(! __webpack_require__.o(exports, name)) {
      Object.defineProperty(exports, name, {
        enumerable: true.get: getter }); }};/ / 3. This Symbol. ToStringTag understood as when we Object. The prototype. ToString. Call (exports), returns [Object Module] type
  __webpack_require__.r = function (exports) {
    if (typeof Symbol! = ='undefined' && Symbol.toStringTag) {
      Object.defineProperty(exports.Symbol.toStringTag, {
        value: 'Module'
      });
    }
    // 4. Exports object with an attribute that indicates that this file is ES6MODULE
    // return to eval below
    Object.defineProperty(exports.'__esModule', {
      value: true
    });
  };
  // Exports object is ES6Module.
  // 12. If so, do we export default X as a default value and import X
  In es6 module, export default X default = X;
  // 14. Export default const X;
  // 15. Default = const X
  // 16. So we use getDefault to return exports.default for you
  // the 17.d method overrides the getter method.
  Getter is now getDefault(). 'a' is the name of one of the arguments we named
  // 19. Let's take a look inside the.d method
  __webpack_require__.n = function (module) {
    var getter = module && module.__esModule ?
      function getDefault() {
        return module['default'];
      } :
      function getModuleExports() {
        return module;
      };
    __webpack_require__.d(getter, 'a', getter);
    return getter;
  };
  __webpack_require__.o = function (object, property) {
    return Object.prototype.hasOwnProperty.call(object, property);
  };
  __webpack_require__.p = "";
  return __webpack_require__(__webpack_require__.s = "./src/index.js"); ({})"./src/index.js":
    (function (module, __webpack_exports__, __webpack_require__) {
      "use strict";
      // 1. Since we used import in index.js, WebPack is nice enough to insert a line of code in front of eval that calls r
      // 2. Let's see what R did
      // 6
      // 7. The __webpack_require__.n method is called, passing the exports object from the imported file
      // SRC /util.js/SRC /util.js/SRC /util.js /util.js
      // 9. So his exports object does not have an attribute called __esModule
      // 10
      eval("__webpack_require__.r(__webpack_exports__); \n/* harmony import */ var _util__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./util */ \"./src/util.js\"); \n/* harmony import */ var _util__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_util__WEBPACK_IMPORTED_MODULE_0__); \n\n\nfunction test() {\n console.log(' I am a small test! ', _util__WEBPACK_IMPORTED_MODULE_0__[\"num\"]); \n}\n\ntest(); \n\n\n//# sourceURL=webpack:///./src/index.js?");
    }),
  This file does not use the ES6 module so it does not insert the __webpack_require__.r method as this one does
  "./src/util.js": (function (module.exports) {
    eval("exports.num = 123; \n\n//# sourceURL=webpack:///./src/util.js?"); })});Copy the code

It’s over… That makes sense

You’re smart enough to know that WebPack loads on demand. What would you do if you were asked to implement it? How many natural ways are there to separate files? Multiple entry? import()? Import (/* prefetch and preload*/) HTML

The principle of

We already know the product of Webpack, and we’ve analyzed the packaged product. What happens when you go from the original file to the built file?


To be continued