前置: 对项目进行一个抽象,首先查看 webpack打包之后的产物。产出一个 IIFE, 此IIFE的传入参数为一个由多个 js bundle 组成的对象,类似下面的抽象
(fucntion(module){
// 实现__webpack_require__
})
({
'/src/index': function(module, __webpack_exports__, __webpack_require__){
var xx = __webpack_require__('/src/test')
console.log('xx', xx)
},
'/src/test': function(module, __webpack_exports__, __webpack_require__){
console.log('title')
},
})
本质上 webpack 是一个打包器,可以打包多种规范的js文件,webpack在打包之后,自己实现了一套自己的 commonjs 规范,核心就是 webpack_require 和 webpack_exports, 此处不是今天的重点。 请记住: 每一个需要加载的模块(对于webpack 编译之后来说,就是每一个 module 都有自己的 webpack_exports、webpack_require), 我们的属性和方法通常都是挂载在__webpack_exports__ 上面。
当我们使用import('./xxx.js')时,本质上就是在使用 Dynamic import,动态加载一个异步的模块,此时我们就会想到 webpack 是如何实现异步加载的 如图所示
可以看到 webpack 最终打包出来的结果是通过 webpack_require.e 来加载一个异步模块,之后使用 webpack_require.t 来解析这个被加载的异步模块。(webpack_require.t 已经在之前的文章写过)
webpack_require.e解析
- 可以看到首先 声明一个对象 installedChunks = {0: 0}
- 之后使用 requireEnsure 这个方法传入一个 chunkId 来调用
- 在requireEnsure 方法内部 声明 一个 promises 数组 (第 78 行)
- 去 installedChunks 这个对象里面 通过 chunkId 来拿一下 当前 chunk 的加载状态 (在第 79行 执行 逻辑:判断当前 chunk 是不是已经被加载过了, 主要看 拿到的值 是不是 为 0 作为 判断依据)
- 此时,对应我们这里的场景 第 79行的 installedChunkData 为 undefined, 所以进入第 87行的 else 判断
- 声明一个 promise 实列,并且让 installedChunkData = installedChunks[chunkdId] = [resolve, reject] (此时 installedChunks[chunkdId] 是一个数组,里面有两个元素,分为对应的是 promise 的resolve reject 两个方法)
- 在执行完 第90行之后, 此时 installedChunks 这个数组里面就有3个元素了,分别对应的是 installedChunks[chunkdId] = [resolve, reject, promise]
- 第 93 行动态一个 script 标签,从第94行 到 第126行, 都是设置这个动态 script 加载的一些逻辑,可以暂时忽略
- 第 127 行,在 head 里面插入 当前 动态创建的 script 标签
- 在第 130 行,使用 Promise.all(promises), 将 这个 promises 数组触发(promises 这个数组 里面包含了 第 87行 声明的promise 实列)
- 此时已经出发了 异步加载的逻辑,在 http的 加持下,可以请求异步加载的 js 文件
12. 当 这个 异步的js文件请求完成之后,触发了 window["webpackJsonp"]的 push 方法 (push 方法内部接收一个 二维数组,数组第一项 为 chunkId, 第二项为 chunk的 内容)
- 在 第30行 第31行 分别是设置 window["webpackJsonp"]方法 和 push方法
- 当 这个 异步的js文件请求完成之后,触发了 window["webpackJsonp"]的 push 方法, 也就是 触发了 webpackJsonpCallback 这个方法。
- 在这个方法的 第4行 第5行 分别拿到了 chunkId 和 chunkId 对应的内容
- 第14行 把 之前的 installedChunks这个数组的 第0项 给 声明的 resolves 数组(installedChunks这个数组就是上面的 webpack_require.e 解析第 6点 对应上)
- 分别循环 chunkId 和 chunkId对应的内容,在第 16行 把 被循环的 chunkId 的加载状态置为0 (installedChunks[chunkId] = 0) 和上面的 webpack_require.e 解析第 4点 对应上
- 循环 chunkId对应的内容 也就是 moreModules,把 moreModules对象 每个值 赋值给 modules (modules 是 webpack 初始化运行时传入的 modules,也就是同步加载的 所有 module)
- 此时已经接近尾声。 在第 26行 执行 resolves 这个数组的每一个方法 (其实就是执行 installedChunks 数组的 第0项,也就是 上面的 webpack_require.e 解析第 7 点,同时也是 上面的 webpack_require.e 解析第 6 点 声明的 promise ,并且将这个promise 置为 完成的 状态)
- 以上,就完成了 一个异步 js 的加载,并且让其为完成状态的 promise,后续在 webpack的 打包里面将 触发 webpack_require.t 方法,对这个 异步的 js 进行 webpack内部的模块解析 和 封装(统一封装为 esmodle 规范 加以使用)