基础概念

Module

Webpack 构建的最小单位

Chunk

Module 的集合,可通过 splitChunks 插件定义分包方式

加载过程

假设存在一个模块 imported.js ,被入口文件 app.js 异步导入,文件结构与内容如下

// imported.js
export default {
    test: 'this is test'
}
// app.js
(async () => {
    const module = (await import("./imported"))

    console.log(module) // Module: {default: { test: ... }}
})();

以上代码构建后,导入语句将被转换为

const test = (await __webpack_require__.e("src_imported_js").then(__webpack_require__.bind(__webpack_require__, "./src/imported.js")))

可以看出异步模块的加载首先要经过 __webpack_require__.e 处理,之后再由 __webpack_require__ 加载模块并缓存

webpack_require.e

此函数需要传入一个 ChunkId,再将其传入 __webpack_require__.f 内的每一个函数,等所有的promises 都 resolved 后,返回所有 promises resovled 的结果数组

__webpack_require__.f 内置了不同 Chunk 的加载器,每个加载器的签名都是 (chunkId, promises)。之所以要传入 promises,是因为加载的 Chunk 可能还需要再加载其它的 Chunk


__webpack_require__.e = (chunkId) => {
    return Promise.all(Object.keys(__webpack_require__.f).reduce((promises, key) => {
        __webpack_require__.f[key](chunkId, promises);
        return promises;
    }, []));
};

webpack_require.f.j

这里暂时只关心 __webpack_require__.f 内其中一个异步 Chunk 加载函数,它的作用如下(进行了改写,不改变核心流程)

__webpack_require__.f.j = (chunkId, promises) => {
    // JSONP chunk loading for javascript
    var installedChunkData = installedChunks[chunkId] || undefined;
    if (installedChunkData !== 0) { // 0 means "already installed".

        // a Promise means "currently loading".
        if (installedChunkData) {
            promises.push(installedChunkData[2]);

        } else {
            // setup Promise in chunk cache
            var promise = new Promise((resolve, reject) => (installedChunkData = installedChunks[chunkId] = [resolve, reject]));
            promises.push(installedChunkData[2] = promise);

            // start chunk loading
            var error = new Error();
            var loadingEnded = () => {
                installedChunkData = installedChunks[chunkId];
                if (installedChunkData !== 0) installedChunks[chunkId] = undefined;
                if (installedChunkData) {
                    installedChunkData[1](error);
                }

            };
            __webpack_require__.l(chunkUrl, loadingEnded, chunkId);
        }

    }
};
  1. 判断要加载的 Chunk 是否已缓存或正在加载中,若结果为假则进入下一步
    1. 若 Chunk 未进行加载,将创建一个 Promise,依次将 resolve、reject 和创建的 Promise 放到一个数组里,并挂载到 installedChunks[chunkId] 上。
    2. 之后创建一个加载回调函数 loadingEnded ,若 installedChunks[chunkId] 不为整数 0,则置空加载状态,并抛出错误
    3. 调用 __webpack_require__.l 正式异步加载 Chunk