js如何替换object某个key_聊聊 webpack 异步加载(二):webpack 如何处理 import()

上篇文章解释了webpack 是怎么加载拆包后的代码的,这篇文章解释下 webpack 如何处理 import()。

import() 包含的代码被 webpack 当作 chunk 处理,同样也是通过 window["webapckJsonp"] 来进行加载。有了上篇文章的讲解,本篇理解起来会很容易。讲解之前再回顾下 webpack bootstrap 代码中非常重要的四个缓存变量

8e592d5a4b550de0f74963061773afcf.png
  • ​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() 被转换后的代码

ae877b07df49e956bc87cdb244678c4f.png

2c2ff3d294a51a8e453d441240739178.png

文件路径被替换成了 chunkId,作为参数调用了__webpack_require__.e,重点研究下 __webpack_require__.e 的实现

5153974b107257c09c0fc0eb651c988f.png

再来重复下, ​installedChunks​ 一个对象,​key​ 为 ​chunk id​,​value​ 有四种情况:

    • undefined:chunk not loaded
    • null:chunk preloaded/prefetched(本篇不涉及)
    • Promise:chunk loading
    • 0:chunk loaded

现在看一下 __webpack_require__.e 干了什么

  • 判断 installedChunks[chunkId] 是否已经被加载,如果已经被加载,直接返回 Promise.all([])
  • 判断 installedChunks[chunkId] 是否在加载中,如果在加载中,把表示加载中的 promise 添加到 promises 数组中
  • 没有加载的话,创建 promise,并赋值:installedChunks[chunkId] = [resolve, reject],添加到promises 数组中
  • 动态创建 script 标签,添加 onerror 以及 onload 事件,并进行加载超时的处理

951335e564ee8ee772fd935cc0dc2f7f.png
    • 加载成功或者失败都会清除加载超时的处理函数
    • 如果加载没有成功,构造 error 信息。还记得 installedChunks[chunkId] = [resolve, reject] 吗?这个时候就派上用场了,chunk[1](error) 即为 reject(error),触发 promise 的 reject
  • __webpack_require__.e 返回 prmises 数组,等待 chunk 加载完成

webpackJsonpCallback

什么时候 promise 会 resolve 呢?code spliting 加载代码时,webpack 的 bootstrap 已经运行过,也就是说 window["webapckJsonp"] 的 push 方法已经被改写成了 webpackJsonpCallback

5dccf49baf07a864cf36b23da5ccd942.png

352769e4da89934675b117e087347452.png

现在以 code spliting 的角度再来走一遍下面的逻辑

a7750f07718a7d306cc641046e91db95.png
  • 参数 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 加载完毕

57c5e77c8005294acc0a879be72f80ea.png

执行 __webpack_require__(moduId),加载入口代码,从而 import() 逻辑加载完毕

依然画个流程图总结一下

89a9aeb7f72e360eb86b17d18916bde2.png

推荐阅读

​聊聊 webpack 异步加载(一):webpack 如何加载拆包后的代码​

​聊聊 webpack 异步加载(三):webpack 如何做到持久化缓存​

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值