了解 Webpack 的启动代码对于分析模块和 chunk 是非常有帮助的。

Webpack 启动代码

首先从最简单的情况入手,在入口文件 index.js 中简单引用了一个模块中的方法:

// index.js
import {func1} from './module1';

// module1.js
export function func1() {
    console.log('func1');
}

根据以上入口文件,在 target: web(默认情况)下生成的 chunk 文件只有一个。观察其中包含的 Webpack 启动代码,可以发现由一个 IIFE 构成,其中传入的参数是一个方法数组。从其中每个方法的签名可以看出,类似 AMD。很明显,module1.js 模块的全部代码都在模块1中,而模块0对应 index.js,引用了模块1。另外有一点值得注意,module1.js 中的方法并没有被真正使用到,注释 /* unused harmony export func1 */ 也反映了这一点,后续在生产环境配合 UglifyJSPlugin 插件使用时,依托 TreeShaking 技术,这段无用代码会被移除。

(function(modules) { // IIFE
 	// 暂时省略处理模块的逻辑
})
([
/* 模块0 */
(function(module, __webpack_exports__, __webpack_require__) {
    "use strict";
    Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
    /* harmony import */
    var __WEBPACK_IMPORTED_MODULE_0__module1__ = __webpack_require__(1);
}),
/* 模块1 */
(function(module, __webpack_exports__, __webpack_require__) {
    "use strict";
    /* unused harmony export func1 */
    function func1() {
        console.log('func1');
    }
})
]);

现在可以深入表达式方法细节了。首先定义了 __webpack_require__ 方法,然后在方法上挂了一些属性便于快捷访问,最后调用入口模块也就是模块0。官方的注释非常详细,尤其是 require 方法的,就不再赘述了。

// 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: {}
    };

    // 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;
}

// 省略挂载属性...

// Load entry module and return exports
return __webpack_require__(__webpack_require__.s = 0);

在方法上挂载的属性中,d 用来处理常量

// 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 = "";
// index.js

import(/* webpackChunkName: "lodash" */ 'lodash').then(_ => {
    return _.version();
});

在 Webpack 中,chunk 一共有三类

参考资料