- Demo代码地址: github.com/mbaxszy7/we…
- 运行项目: nom run dev
要搞懂webpack 动态import的实现需要先搞懂webpack打包后产生的代码
webpack输出代码分析
- 代码框架分析
- webpack打包后的代码其实是一个自执行函数,该函数的modules参数就是一个对象,该对象所有的key是文件的路径,对应的value是该文件转换后的代码。以index.js为例,对应的代码片段是:

从以上代码可以看出,webpack替换了import 和 index中依赖的模块Hello。
- 再看自执行函数的匿名函数部分

简化以后的代码如下:

所以这个自执行函数会运行__webpack_require__(0)(第0项内容就是去加载 src/index.js 中的代码)
- 具体函数分析
- webpack_require

__webpack_require__接受一个moduleId,就是modules参数的key。__webpack_require__内执行的模块的代码就是key对应的value。
- 在此项目中, 当运行到index模块的代码时,会去加载hello模块的代码:webpack_require(/*! ./hello */ “./src/hello.js”)。hello.js打包后的代码如下:

这里多了执行__webpack_require__.r 和 webpack_require.e。可以看到打包后的hello.js中有这样有个promise链:
__webpack_require__.e(/*! import() */ 0)
.then(__webpack_require__.bind(null, /*! ./async */ "./src/async.js"))
先去加载了bundle 0 也就是0.bundle.js, 也就是webpack code splitting 出的async.js。然后(then)执行async.js的代码。
-
webpack_require.r

-
webpack_require.e 先来看一下__webpack_require__.e内部的代码:

大致概括一下__webpack_require__.e的代码: 根据传入的chunkId,先检查是否已经加载过了,再检查是否正在加载, 否则的话创建加载script的promise,然后去加载chunk script。如果script加载失败(超时也算),那么执行chunk promise的reject。
从这步可以看到动态import的实现已经初露端倪。要完整的理解,还需要思考一个问题:上面script加载成功的chunk promise的resolve在什么时候执行?下面来看一下项目async.js打包出来的代码(去掉了一些注释):

这里重点要看window[“webpackJsonp”]。window[“webpackJsonp”] 在之前的自执行函数的匿名函数中有定义。并且window[“webpackJsonp”] 的push方法已经被重写为webpackJsonpCallback。下面就来看一下webpackJsonpCallback这个函数。
- webpackJsonpCallback

webpackJsonpCallback大致就是在做:将传入的chunkid(个人以为叫bundleid更合理)标记为已加载,并将传入的模块挂在到installedChunks对象上(缓存),最终执行 webpack_require.e 函数返回的promise的resolve,注意resolve也是从__webpack_require__.e 函数处理过的installedChunks上取的(installedChunks[chunkId] = [resolve, reject])
总结
实现动态import的主要代码:
async () => {
const res = await import("./async");
console.log(res.default());
}
对应打包后的代码:
async () => {
const res = await __webpack_require__.e(/*! import() */ 0)
.then(__webpack_require__.bind(null, /*! ./async */ "./src/
async.js"));
console.log(res.default());
}
- 执行流程:
-
先执行__webpack_require__.e(0),把动态import的promise挂载到installedChunks上, 创建script 加载0.bundle.js, 返回promise
-
如果加载(超时也是)失败,在onScriptComplete中reject;如果加载成功,则执行加载过来的js:执行 window[webpackJsonp] 上的push方法(已经被重写为webpackJsonpCallback),将动态加载的模块(0.bundle.j)标记为已加载,并将模块(async.js)对应的代码挂载到modules参数上,最后resolve异步加载的模块。
-
在then后执行(webpack_require)挂载到modules参数上对应的模块(async.js)的代码
通过分析 webpack打包后的模板代码,可以看到webpack用IIFE的形式将所有模块作为modules参数传入,从entry模块开始依次执行modules,巧妙的处理了动态加载的模块。用 webpack_require 抹平了import和require之间的差异。