上一篇文章介绍了web包同步加载模块的原理。 本文介绍了web包异步加载模块。
异步加载模块
还是做一些准备。
首先定义依赖模块。 math.js。 math.js使用ES6 module导出了两个函数add和minus。
导出函数add (a,b ) {
返回a b;
}
导出函数(a,b ) {
返回a-b;
}
接下来,定义一个名为index.js的门户模块。 index.js通过import函数导入了math.js,并调用了内部add和minus方法。
import ()./math ).then ) ) math={
控制台. log (math.add (2,1 );
控制台. log (math.MINUS (2,1 );
);
最后,定义一个名为webpack.config.js的配置文件。 内容与webpack同步加载模块相同。
常数路径=require (' path );
module.exports={
模式: '开发',
devtool: 'source-map ',
entry:path.join(__dirname,' index.js ',
output: {
filename: 'main.js ',
path:path.join(_dirname,' dist ' ) )。
}
(;
在根目录下运行web pack-- config web pack.config.js时,将在dist目录下显示最终产物,在dist目录下除了main.js外还添加0.main.js 通常将main.js称为同步chunk,将0.main.js称为异步chunk。
让我们先看看main.js文件中的代码。 main.js代码片段
首先是webpackBootstrap的函数主体。 与同步加载模块相比,添加了许多内容: webpackJsonpCallback函数:异步加载chunk后的jsonp回调函数
installedChunks对象:缓存chunk的加载状态,main表示入口模块,0表示模块已加载。 由于入口模块是同步chunk,因此默认已加载
jsonpScriptSrc函数:连接chunk请求地址
__webpack__require__.e函数:异步加载chunk函数
jsonp初始化代码:初始化jsonp相关配置
接下来是webapackBootstrap的参数部分。 与同步加载模块不同,它将删除依赖模块0.0.main.js。 main.js的内容将移动到异步chunk文件0.main.js中。
最后,使用__webpack_require__.e异步下载依赖模块math.js,math.js下载完成后,__webpack_require__
看这里,应该有一种初步感觉,webpack异步加载模块其实是通过jsonp方式实现的。
接下来,我们来看看__webpack_require__.e_的函数实现。 __webpack_requiore__.e函数的实现
__webpack_require__.e的代码很多,我们来逐一看看吧。 开头定义了promise数组promises,最后在Promise.all中返回promises。 因此,__webpack_require__.e中间的代码实际上是用于处理promises数组的。 具体是怎么操作的,让我们一步一步来看看吧。
首先,是缓存搜索。 installedChunks对象根据chunkId确定是否加载了相应的chunk。 chunk主要有三种状态,分别对应不同的处理。 0 :表示已加载0:chunk。 这种情况下,不进行处理,直接返回
数组表示chunk正在加载。 在这种情况下,缓存的promise对象将被推入promises数组中,并等待chunk加载完成
未定义:表示chunk是第一个加载的。 这种情况下的操作是创建新的promise对象
将新创建的promise对象的reject、resolve方法和promise对象本身存储在installedChunks对象中
将promise对象推入promises数组
/p>接下来是模块加载,模块加载使用 jsonp 的方式,首先会动态创建一个 script 标签,src 指向异步 chunk 地址,然后将 script 标签添加到 head 中,实现异步加载 chunk 的功能。
最后是异常处理,给 script 标签添加了 onloade 和 onerror 事件处理函数onScriptComplete,onScriptComplete 在判断模块加载超时或是加载失败的情况下(缓存的 chunk 不为 0),会调用之前保存的 reject 方法返回模块加载失败的异常,同时还会将 chunk 的缓存标识设置为 undefined,标识未加载。
从前面的流程中我们可以看到,__webpack_require__.e 主要是通过 jsonp 的方式来加载异步 chunk,同时通过 promise 对象来控制异步 chunk 的加载情况:在加载失败的情况下会调用 promise 对象的 reject 方法,在加载成功的情况会执行异步 chunk 文件,也就是 0.main.js。
再来看一下 0.main.js 中的代码。0.main.js 代码片段
0.main.js 中的内容比较简单,主要是调用 window 上挂载的全局数组 webpackJsop 的 push 方法,push 的内容包括两部分:chunkId 数组和依赖模块函数,webpackJsonp 全局数组又是在哪里定义的呢?jsonp 初始化代码
我们回到 webpackBootStrap 函数,可以看到,webpackJsonp 是在 jsonp 初始化代码中定义的,jsonp 初始化代码主要干了以下几件事:定义了一个全局的 webpackJsonp 数组,用来存储所有的 jsonp 回调
将 webpackJsonp 的原生 push 方法改写为了 webpackJsonpCallback
将 webpackJsonp 原生的 push 方法保存为了 parentJsonpFunction
所以,在 0.main.js 中调用 webpackJsonp 的 push 方法,最终执行的是 webpackJsonpCallback 中的代码。
最后,看一下 webpackJsonpCallback 的函数实现。webpackJsonpCallback 函数实现
首先是函数参数,data 参数分为两部分:chunkIds:一个数组,包含当前 chunk 文件依赖的 chunkId,以及自身的 chunkId
moreModule:代表当前 chunk 带来的新模块,也就是咱们前面看到的模块执行函数
接下来是执行逻辑:遍历 chunkIds 数组,判断 installedChunks 对象中的 chunk 是否处于加载中状态,如果返回数组,则表示正在加载中,在这种情况下,会取出缓存的 resolve 方法(installedChunks[chunkId][0]),放到 resolves 数组中,等下统一执行,同时,还会将对应的 chuk 设置为已加载
将 moreModules 合并到 modules 对象中,便于后续同步加载
将 data 参数保存到 webpackJsonp 全局数组中
遍历 resolves 数组,执行前面缓存的 resolve 方法并清空数组,保证该模块加载开始前所有前置依赖内容包括它自身都已经被加载完毕
总结
总结一下,webpack 采用 jsonp 的方式来实现模块的异步加载:通过 __webpack_require__.e 实现动态加载
通过 webpackJsonpCallback 实现异步回调
最后,我们用流程图来总结一下 webpack 异步加载模块的流程。webpack 异步加载模块流程图