webpack 懒加载原理

2,932 阅读2分钟

webpack模块化的原理

webpack的模块化实现和node的commonJs有些类似。

以下代码的require 对应__webpack__require__, require.ensure 对应 webpack_require.e, 与真实打包后的命名不一致, 同时以下过程忽略cache);

webpack打包完的bundle.js, 是一个自执行函数,参数是一个module对象。module对象里是我们在项目里的import或者require的文件,key是文件相对于根路径的path, value是function函数,里面包含了我们在写在文件里的内容。function 有module, export, require这些形参,是我们执行的时候需要传入的。以下示意module里的内容

(function(module) {
	...
}({
'./src/index.js' : function(module, export, require) { ... },
'./src/test.js': function(module, export, require) { ... }
))

自执行函数里有一个require函数用来加载并执行module,返回module.exports对象。以下为简单实现。

function(module) {
    function require(moduleId) {
        let module = {
            moduleId: moduleId,
            exports: {},
            load: false
        }
        
        let function = module[moduleId]; //获取module的function函数
        function.call(module, modlue, module.exports, require);
        module.load = ture;
        return module.exports;
    }
    require('./src/index.js');
}

懒加载的实现

首先import()语句会先被转换 require.ensure。如下

import(/* webpackChunkName: "test" */'./test.js').then();
require.ensure('test').then(require('./test.js')).then()

require.ensure 首先会根据传入chunkId, 去找到文件地址,然后用jsonp的方式,将js加载,同时返回promise等待异步js的下载。

let installedChunks = {};  //记录加载的模块的信息。
function requireEnsure(chunkId) {
    let promises = [];
    let installedChunkData;
    //记录当前模块的promise, 及resolve, reject,用来之后触发promise.
    let promise = new Promise((resolve, reject) => {
        installedChunkData = [resolve, reject];
    });
    installedChunkData[2] = promise;
    installedChunks[chunkId] = installedChunkData;
    //使用jsonp的方式来进行js文件的加载
    let script = document.createElement('script');
    script.src = jsonpScriptSrc(chunkId);
    //用来获取文件的地址jsonpScriptSrc-->
    document.head.append(script)
    return Promise.all(promises);
}

当js文件被加载后返回如下文件内容。虽然显示的是window["webpackJsonp”].push(…)。而其实这个方法被改写了 。 window[“webpackJsonp”].push其实相当于 用webpackJsonpCallback函数去执行。。


(window["webpackJsonp"] = window["webpackJsonp"] || []).push([
  ["test"],
  {
    "./src/test": function (module, __webpack_exports__, __webpack_require__) {
    //...
        };
    },
  },
]);

// 约等于====>
webpackJsonpCallback([
  ["test"],
  {
    "./src/test": function (module, __webpack_exports__, __webpack_require__) {
    //...
    };
},
},
])

webpackJsonpCallback 的主要作用是将获得的模块数据,插入到之前的module对象里,然后将在requireEnsure里的promise用resolve触发完成,使其能正常的被then.


webpackJsonpCallback(data) {
    let chunkIds = data[0];
    let moreModules = data[1];
    let moduleId,
      chunkId,
      i = 0,
      resolves = [];
    for (; i < chunkIds.length; i++) {
      chunkId = chunkIds[i];
      if (installedChunks[chunkId]) {
        resolves.push(installedChunks[chunkId][0]);//对应requre.ensure里出入的resolve;
      }
      installedChunks[chunkId] = 0;//installedChunks里 0表示加载完成了。
    }
    //往module对象里添加当前的module信息
    for (moduleId in moreModules) {
        modules[moduleId] = moreModules[moduleId];
    }
    //触发require.ensure里传入的resolve;
    resolves.forEach(resolve)

}

然后就是正常的模块加载了,稍微不同的是import()加载的模块都会被转成webpack里es6的module的形式。