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的形式。