webpack之异步加载

265 阅读1分钟

前言

webpack将import(chunkId)打包成__webpack_require__.e(chunkId)。首先__webpack_require__.e****会创建一个promise,并且将该promise的resove函数缓存到installedChunks对象中,然后创建script标签加载资源。接着资源加载成功之后,资源中会自动调用webpackJsonpCallback,传入的参数格式是[[chunkId], {moduleId: () => {}}]。该函数做了两个处理:1. 将moduleId作为modules的key,将module函数添加到modules中;2. 调用installedChunks中保存的resolve函数,此时__webpack_require__.e的promise.all变成resolved状态。最终通过__webpack_require(moduleId)的方式返回最终的模块。

const installedChunks = {}; // 保存着promise的resolve函数,类似:{ [chunkId]: resolve }
function _webpack_repuire.e(chunkId) {
    const promises = [];

    const promise = new Promise(function(resolve) {
        // 将resolve函数保存起来
        installedChunks[chunkId] = resolve;
    })
    addScript(); // 创建script,异步加载资源。具体代码省略
    return Promise.all(promises);
}

// 加载的js资源是一个函数执行,该函数在主文件中由webpack定义然后挂载到window上
webpackJsonpCallback([
    [chunId],
    {
        moduleId: (module, __webpack_exports__, __webpack_require__) => {
            __webpack_exports__["default"] = '我们到代码';
        }
    }
])

// 异步资源加载成功之后执行
function webpackJsonpCallback(data) {
    const [chunks, moreModules] = data;
    const resolves = []; 
    for(let i = 0; i <chunks.length; i++) {
        // 将保存在installedChunks中的resolve函数,保存到resolves队列中,等待执行
        const chunkId = chunks[i];
        resolves.push(installedChunks[chunkId])

    }

    for(let moduleId in moreModules) {        
        // 将异步资源中到module函数添加到modules中。modules就是webpack的模块集合,是一个对象
        // 数组,类似[{ [moduleId]: () => {} }]。
        modules[moduleId] = moreModules[moduleId];
    }

    // 开始执行resolve,此时webpack_require.e中的promise.all变成resolved状态
    while(resolves.length) {
        resolves.shift()();
    }
}

import('./utils.js').then(data => { console.log(data) });
// 会被打包成
__webpack_require__.e(chunkId).then(__webpack_require__.bind(null, moduleId)).then(data => { console.log(data) })
// __webpack_require__.e中的Promise.all返回之后,模块已经被注册到modules对象,然后
webpack_require就可以在modules上获取到模块函数,并且执行模块函数,然后返回模块的module.exports
对象。
至此,异步模块加载完成。