webpack 打包初识

发布时间 2023-03-25 23:54:21作者: EGBDFACE

webpack 是用来做什么的,原理是什么

webpack 是一个打包模块的机制,把依赖的模块转化成可以代表这些包的静态文件,识别入口文件和模块依赖来打包代码,自动对代码使用的方式如 commonJS、amd、es6 import 进行分析,webpack 做的就是分析代码、转换代码、编译代码、输出代码。webpack 本身是一个 node 的模块,故 webpack.config.js 是以 commonJS 形式书写。

从输入到输出

webpack 中每个模块都有一个唯一的 ID ,是从 0 开始递增的。整个打包后的 bundle.js 是一个立即执行函数,参数是一个数组,数组的每一项都为一个 function,其内容则是每个模块内容,并按照 require (或其他模块化引入)的顺序排列。

commonJS

如下两个文件 a.jsb.js其在 webpack@2 下打包之后的文件 bundle.js与在 webpack@4.41.5 下打包之后的文件 bundle.js

// a.js
var b = require('./b.js');

console.log('a');

b.b1();
// b.js
exports.b1 = function () {
    console.log('b1')
  };
  
  exports.b2 = function () {
    console.log('b2')
};
// webpack@2
// bundle.js
/******/ (function(modules) { // webpackBootstrap
/******/    // The module cache
/******/    var installedModules = {};

/******/    // 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] = {
/******/            exports: {},
/******/            id: moduleId,
/******/            loaded: false
/******/        };

/******/        // Execute the module function
/******/        modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);

/******/        // Flag the module as loaded
/******/        module.loaded = true;

/******/        // Return the exports of the module
/******/        return module.exports;
/******/    }


/******/    // expose the modules object (__webpack_modules__)
/******/    __webpack_require__.m = modules;

/******/    // expose the module cache
/******/    __webpack_require__.c = installedModules;

/******/    // __webpack_public_path__
/******/    __webpack_require__.p = "";

/******/    // Load entry module and return exports
/******/    return __webpack_require__(0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ function(module, exports, __webpack_require__) {

    var b = __webpack_require__(1);

    console.log('a');

    b.b1();


/***/ },
/* 1 */
/***/ function(module, exports) {

    exports.b1 = function () {
      console.log('b1')
    };

    exports.b2 = function () {
      console.log('b2')
    };

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

_webpack_require 是模块加载函数,接收的参数即为模块 ID ,这个函数中调用了 module.call,参数为 module.exports,module,module.exports,_webpack_require,相当于执行传入这个匿名函数中的数组项,并将其中的 exports 绑定到 module.exports 中。对于 b.js ,webpack 将整个文件包裹了起来,添加了三个参数变量,将 a.js 中 export 属性都绑定到了参数 exports 上,在 __webpack_require 函数中返回这个 export 对象从而导出 a.js 中返回的对象。对于 a.js ,使用的方式即从具有导入模块的数组入手获取依赖的对象,之后使用这个对象。
可以看到 webpack@4.41.5 相对于老版本的 webpack 基本相同,只不过增加一些属性,其中就有可以进行 commonJS 与 es6 module 混用模块的逻辑等。

ES 6 module

如下两个文件 app.jsa.js ,在 webpack@2 下打包之后的文件 bundle.js与在 webpack@4.41.5 下打包之后的文件 bundle.js

// a.js

let a = 1
const b = 2
export function log(val) {
 console.log(val)
}
export default{
 a:a,
 b:b
}
// app.js

import tt from './a'
import {log} from './a'
console.log(tt.b)
log(tt.b)
// webpack@2
// bundle.js
(function(modules) { // webpackBootstrap
    // The module cache
    var installedModules = {};
    
    // 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: {}
      };
    
      // 执行了导入的module,把export的值都放到module.exports中
      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;
    }
    
    
    // expose the modules object (__webpack_modules__)
    __webpack_require__.m = modules;
    
    // expose the module cache
    __webpack_require__.c = installedModules;
    
    // identity function for calling harmony imports with the correct context
    __webpack_require__.i = function(value) { return value; };
    
    // define getter function for harmony exports
    __webpack_require__.d = function(exports, name, getter) {
      if(!__webpack_require__.o(exports, name)) {
        Object.defineProperty(exports, name, {
          configurable: false,
          enumerable: true,
          get: getter
        });
      }
    };
    
    // getDefaultExport function for compatibility with non-harmony modules
    __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;
    };
    
    // Object.prototype.hasOwnProperty.call
    __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
    
    // __webpack_public_path__
    __webpack_require__.p = "";
    
    // Load entry module and return exports
    return __webpack_require__(__webpack_require__.s = 1);
    })
      /************************************************************************/
    ([
      /* 0 */
      /***/ (function(module, __webpack_exports__, __webpack_require__) {
    
      "use strict";
      /* harmony export (immutable) */ __webpack_exports__["b"] = log;
      let a = 1
      const b = 2
      function log(val) {
        console.log(val)
      }
      /* harmony default export */ __webpack_exports__["a"] = {
        a:a,
        b:b
      };
    
    
      /***/ }),
      /* 1 */
      /***/ (function(module, __webpack_exports__, __webpack_require__) {
    
      "use strict";
      Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
      /* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__a__ = __webpack_require__(0);
    
    
      console.log(__WEBPACK_IMPORTED_MODULE_0__a__["a" /* default */].b)
      __webpack_require__.i(__WEBPACK_IMPORTED_MODULE_0__a__["b" /* log */])(__WEBPACK_IMPORTED_MODULE_0__a__["a" /* default */].b)
    
    
      /***/ })
    ]);
// webpack@4.41.5
// bundle.js
// 感叹号用于构建立即执行函数,取代 (function(){})() 的写法, 用括号包裹解析器会以函数表达式的方式调用定义函数,感叹号也可以
// 也可以将感叹号换为其他的一元运算符,如 ~,!!!,~~~ 甚至其组合
! 
function(e) {
    var t = {};
    function r(n) {
        if (t[n]) return t[n].exports;
        var o = t[n] = {
            i: n,
            l: !1,
            exports: {}
        };
        return e[n].call(o.exports, o, o.exports, r),
        o.l = !0,
        o.exports
    }
    r.m = e,
    r.c = t,
    r.d = function(e, t, n) {
        r.o(e, t) || Object.defineProperty(e, t, {
            enumerable: !0,
            get: n
        })
    },
    r.r = function(e) {
        "undefined" != typeof Symbol && Symbol.toStringTag && Object.defineProperty(e, Symbol.toStringTag, {
            value: "Module"
        }),
        Object.defineProperty(e, "__esModule", {
            value: !0
        })
    },
    r.t = function(e, t) {
        if (1 & t && (e = r(e)), 8 & t) return e;
        if (4 & t && "object" == typeof e && e && e.__esModule) return e;
        var n = Object.create(null);
        if (r.r(n), Object.defineProperty(n, "default", {
            enumerable: !0,
            value: e
        }), 2 & t && "string" != typeof e) for (var o in e) r.d(n, o,
        function(t) {
            return e[t]
        }.bind(null, o));
        return n
    },
    r.n = function(e) {
        var t = e && e.__esModule ?
        function() {
            return e.
        default
        }:
        function() {
            return e
        };
        return r.d(t, "a", t),
        t
    },
    r.o = function(e, t) {
        return Object.prototype.hasOwnProperty.call(e, t)
    },
    r.p = "",
    r(r.s = 0)
} ([function(e, t, r) {
    "use strict";
    r.r(t);
    var n, o = 2;
    console.log(o),
    n = o,
    console.log(n)
}]);

对于 app.js 与 a.js 的处理和 commonJS 在 webpack@2 上基本相同。因为是 es 6 模块,故首先通过 Object.defineProperty__webpack_exports__上添加了属性 __esModule ,值为 true 表示这是一个 es 模块。需要注意的是在 webpack@4.41.5 中,其中立即函数执行的参数变为了一个???

参考: