在单页应用中,常常使用 webpack 的 动态导入 功能来异步加载模块,从而减小部分文件的体积。咱们能够经过webpack 提供的 import()
和 require.ensure
两个 API 来使用该功能。因为两个方法根本实现都是相同的,本文的示例都基于 import()
方法。javascript
webpack 4.35.3
java
这边用一个最简单的例子,有两个文件,
index.js
为入口文件,该文件使用import()
来动态导入async.js
文件。webpack
index.js
web
async.js
promise
咱们执行 webpack --mode=development
来得到编译后文件 main.js
与 0.js
。其中,main.js
包含 index.js
代码,0.js
包含 async.js
代码。bash
首先,main.js
文件做为整个应用的入口,咱们来看看里面有什么东西。异步
咱们先忽略详细的代码逻辑,总体看下来,发现这个文件其实就是一个自执行函数,该函数把 转化后的 index.js
和 文件路径 组成一个 modules 传给主函数。async
主函数中,会执行 __webpack_require__(__webpack_require__.s = "./src/index.js");
来初始化入口模块。函数
这个函数的做用就是加载而且执行指定的模块,而且返回模块的 module.exports
。 这边执行模块调用了 modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
来执行以前传入的 module,也就是 转换后的index.js
。fetch
咱们抽离当中的代码看一下。
发现import
被转化成了 __webpack_require__.e(/*! import() */ 0)
,咱们顺藤摸瓜,继续来看看 __webpack_require__.e
作了些什么。
代码可能有点眼花,看下来无非就是作了这么一件事情。
installedChunks
检查是否加载过该 chunkJSONP
请求去加载 chunk当 Promise 返回以后,就会继续执行咱们以前的异步请求回调
__webpack_require__.e(/*! import() */ 0)
.then(
__webpack_require__.bind(null, /*! ./async */ "./src/async.js")
)...
复制代码
这里直接调用了 __webpack_require__
去加载咱们的 异步模块
。
这里就有两个问题?
__webpack_require__
是根据咱们以前传入的 modules
来获取 module
的,可是,在 __webpack_require__.e
中并无看到有对 modules
执行操做的代码。那 modules
究竟是何时被更新的呢?promise
把 resolve
和 reject
所有存入了 installedChunks
中, 并无在获取异步chunk成功的onload
回调中执行 resolve
,那么,resolve
是何时被执行的呢?var promise = new Promise(function(resolve, reject) {
installedChunkData = installedChunks[chunkId] = [resolve, reject];
});
复制代码
带着疑问,咱们来看一下加载的 0.js
中的内容。
0.js
这边逻辑也很简单,只是往window["webpackJsonp"]
里面 push chunkId
和 含有的modules
。咱们搜一下 window["webpackJsonp"]
, 发如今 main.js
中有这么一段代码。
这边用自定义的 webpackJsonpCallback
函数替换了 window["webpackJsonp"]
的 push
方法。因此说,咱们以前 0.js
执行的 push
其实就是执行了自定义的 webpackJsonpCallback
函数。
能够看到,webpackJsonpCallback 作了2件事情。
installedChunks
中的 resolve
, 让 import()
得以继续执行。chunk
中含有的 模块所有注册到 modules
变量中。如今,咱们终于理清了异步加载的所有流程。
其实,以上的代码只是最简单的状况,随着代码的不一样,生成的函数具体内容也会有所差别。好比,咱们添加预加载代码 import(/* webpackPrefetch: true */'./preload');
, 生成的 main.js
文件中才会有支持该功能的代码,不难看出,webpack此举是为了控制生成对文件大小,对具体细节感兴趣的同窗,能够本地尝试一下。