作者 | vayne
转载请注明出处: juejin.cn/user/272334…
上篇文章解释了webpack 是怎么加载拆包后的代码的,这篇文章解释下 webpack 如何处理 import()。import() 包含的代码被 webpack 当作 chunk 处理,同样也是通过 window["webapckJsonp"] 来进行加载。有了上篇文章的讲解,本篇理解起来会很容易。讲解之前再回顾下 webpack bootstrap 代码中非常重要的四个缓存变量
-
modules:缓存 module 代码块,每个 module 有一个 id,开发环境默认以 module 所在文件的文件名标识,生产环境默认以一个数字标识。modules 是一个 object, key 为 module id,value 为对应 module 的源代码块。
-
installedModules:缓存已经加载过的 module,简单理解就是已经运行了源码中 import somemodule from 'xxx' 这样的语句。installedModules 是一个 object, key 为 module id,value 为对应 module 导出的变量。(跟 modules 的 value 是不一样的,这里的 value 保存的是 module 对应的代码中 export 的变量)
-
installedChunks:缓存已经加载过的 chunk,简单理解就是把其他 js 文件中的 chunk 包含的 modules 同步到了当前文件中。每个 chunk 有一个 id,默认以一个数字标识。installedChunks 也是一个对象,key 为 chunk id,value 有四种情况:
-
undefined:chunk not loaded
-
null:chunk preloaded/prefetched
-
Promise:chunk loading
-
0:chunk loaded
-
deferredModules:缓存运行当前 web app 需要的 chunk id 以及入口 module id(截图中 299 标识入口 module 的 id,0 和 1 标识运行必需的另外两个 chunk 的 id),比如,react 和 react-dom 被单独打包到了另外的 js 中,入口文件需要等待 react 和 react-dom 加载成功之后才能运行(这篇文章不涉及)。
import() --> webpack_require.e
首先看一下 import() 被转换后的代码
文件路径被替换成了 chunkId,作为参数调用了__webpack_require__.e,重点研究下 webpack_require.e 的实现
再来重复下, installedChunks 一个对象,key 为 chunk id,value 有四种情况:
-
undefined:chunk not loaded
-
null:chunk preloaded/prefetched(本篇不涉及)
-
Promise:chunk loading
-
0:chunk loaded
-
判断 installedChunks[chunkId] 是否已经被加载,如果已经被加载,直接返回 Promise.all([])
-
判断 installedChunks[chunkId] 是否在加载中,如果在加载中,把表示加载中的 promise 添加到 promises 数组中
-
没有加载的话,创建 promise,并赋值:installedChunks[chunkId] = [resolve, reject],添加到promises 数组中
-
动态创建 script 标签,添加 onerror 以及 onload 事件,并进行加载超时的处理
-
加载成功或者失败都会清除加载超时的处理函数
-
如果加载没有成功,构造 error 信息。还记得 installedChunks[chunkId] = [resolve, reject] 吗?这个时候就派上用场了,chunk1 即为 reject(error),触发 promise 的 reject
-
webpack_require.e 返回 prmises 数组,等待 chunk 加载完成
webpackJsonpCallback
什么时候 promise 会 resolve 呢?code spliting 加载代码时,webpack 的 bootstrap 已经运行过,也就是说 window["webapckJsonp"] 的 push 方法已经被改写成了 webpackJsonpCallback
现在以 code spliting 的角度再来走一遍下面的逻辑
-
参数 data 的形式为 [ chunkId[], modules[] ]
-
第一个 for 循环判断 installedChunks[chunkId] 是否在加载中,这个时候 installedChunks[chunkId] = [resolve, reject]。执行当前函数时,代表当前 chunk 已经被正确加载,因此 installedChunks[chunkId][0] 表示的即为 resolve
-
第二个 for 循环把当前 chunk 包含的 module 保存到入口文件的 modules 变量
-
执行 resolve(),webpack_require.e 返回 fullfilled 态的 promise,表示 chunk 加载完毕
执行 webpack_require(moduId),加载入口代码,从而 import() 逻辑加载完毕
依然画个流程图总结一下