懒加载模板代码
src目录下文件:
入口文件index.js,点击按钮获取并加载login.js文件
// index.jslet oBtn = document.getElementById('btn')oBtn.addEventListener('click', function () {import(/*webpackChunkName: "login"*/'./login.js').then((login) => {console.log(login)})})console.log('index.js执行了')// login.jsmodule.exports = "懒加载导出内容"
打包结果
最终打包结果生成 build.js 和 login.build.js
先看看作为立即执行函数的参数是怎么样的。当点击按钮时,加载login.js
- 调用webpack_require.e方法,创建script标签并引入login.js
- 调用webpack_require.t方法,执行login.js中的代码
(function (modules) {...})({"./src/index.js":(function (module, exports, __webpack_require__) {let oBtn = document.getElementById('btn')oBtn.addEventListener('click', function () {__webpack_require__.e(/*! import() | login */ "login").then(__webpack_require__.t.bind(null, /*! ./login.js */ "./src/login.js", 7)).then((login) => {console.log(login)})})console.log('index.js执行了')})});
与以往不同的是,立即执行函数会设置window[“webpackJsonp”],并重写window[“webpackJsonp”]中的push方法
(function (modules) {...// 设置window["webpackJsonp"]var jsonpArray = window["webpackJsonp"] = window["webpackJsonp"] || [];// 保存原有的push方法var oldJsonpFunction = jsonpArray.push.bind(jsonpArray);// 因为保存的是地址,所以修改jsonpArray.push时候也会修改window["webpackJsonp"].push// webpackJsonpCallback是懒加载的核心函数jsonpArray.push = webpackJsonpCallback;// 深拷贝一份jsonpArrayjsonpArray = jsonpArray.slice();...})({...})
webpack_require.e函数
__webpack_require__.e = function requireEnsure(chunkId) {// 申明promises,数组中保存Promise类型的函数// 最终通过返回Promise.all([promises])var promises = [];// 获取installedChunks注册的函数var installedChunkData = installedChunks[chunkId];// 0 => 已经执行完毕,undefined => 未加载// null => 预加载,promise => 加载中if (installedChunkData !== 0) {// 如果是加载中if (installedChunkData) {promises.push(installedChunkData[2]);} else {// 申明promise// 并设置installedChunkData[2] = promise// 保存完整的promisevar promise = new Promise(function (resolve, reject) {installedChunkData = installedChunks[chunkId] = [resolve, reject];});promises.push(installedChunkData[2] = promise);// 创建script标签var script = document.createElement('script');// jsonpScriptSrc函数,返回__webpack_require__.p + "" + chunkId + ".built.js"// __webpack_require__.p => webpack中publicPath配置项script.src = jsonpScriptSrc(chunkId);// 往head中添加script标签document.head.appendChild(script);}}// 返回promise.allreturn Promise.all(promises);}
webpack_require.t函数
主要内容就是执行login.js
// a & b => 通过转换二进制进行比较__webpack_require__.t = function (value, mode) {// 执行login.jsif (mode & 1) value = __webpack_require__(value);// 返回执行的结果if (mode & 8) return value;if ((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;var ns = Object.create(null);__webpack_require__.r(ns);// 绑定defaultObject.defineProperty(ns, 'default', { enumerable: true, value: value });// 如果是对象类型if (mode & 2 && typeof value != 'string') for (var key in value) __webpack_require__.d(ns, key, function (key) { return value[key]; }.bind(null, key));return ns;};
login.js中的代码
// 在之前window["webpackJsonp"].push已经被重写为webpackJsonpCallback函数// 调用webpackJsonpCallback函数(window["webpackJsonp"] = window["webpackJsonp"] || []).push([["login"], {"./src/login.js":(function (module, exports) {module.exports = "懒加载导出内容"})}]);
webpackJsonpCallback函数
function webpackJsonpCallback(data) {// chunkIds => "login"var chunkIds = data[0];// moreModules => 用对象包裹的执行函数var moreModules = data[1];var moduleId, chunkId, i = 0, resolves = [];// 循环chunkIdsfor (; i < chunkIds.length; i++) {// 获取每一项chunkId = chunkIds[i];// 往resolves中添加Promise中的resolveif (Object.prototype.hasOwnProperty.call(installedChunks, chunkId) && installedChunks[chunkId]) {resolves.push(installedChunks[chunkId][0]);}// 标记为0,即已执行installedChunks[chunkId] = 0;}// 循环moreModulesfor (moduleId in moreModules) {if (Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {// 设置modules中的moduleId为对应函数// 方便__webpack_require__调用时,可以获取到对应的函数modules[moduleId] = moreModules[moduleId];}}if (parentJsonpFunction) parentJsonpFunction(data);// 递归执行resolve函数while (resolves.length) {resolves.shift()();}};
由此,webpack懒加载便结束了。核心就是
- 重写window[“webpackJsonp”].push方法
- 创建并引入script
