webpack 5 prefetch runtime 代码解析

1,067 阅读1分钟

Webpack 从 4.6.0+ 开始支持 prefetch,用法如下:

使用 prefetch

const loadAsync = () =>
  import(/* webpackChunkName:"async" */ /* webpackPrefetch: true */ './async');

异步模块里面依赖了异步模块并且使用了 webpackPrefetch 才会在打包出来的 runtime 中增加 prefetch 的逻辑。

要理解这个逻辑先把整个 webpack runtime 梳理一遍。

webpack runtime 处理异步 chunk 的逻辑

这里简单总结为一个图:

image.png

webpack runtime 处理异步加载 import() 这种是在 __webpack_require__.e 中。

__webpack_require__.e 内部通过 reduce 把多个挂载在 __webpack_require__.f 对象下的处理函数迭代一次,比如

  • __webpack_require__.f.j 通过 head 插入 script 方式加载 js,里面的逻辑有点复杂
  • __webpack_require__.f.miniCSS 加载 css
  • __webpack_require__.f.prefetch 处理 prefetch

可以把 webpack 通过 reduce 处理 __webpack_require__.f 当作处理中间件,webpack 的插件可以通过在 __webpack_require__.f 挂载新的处理函数实现加载模块过程中定制功能。

webpack 打包的时候会分析出来加了 webpackPrefetch: true 的模块加入到 chunkToChildrenMap 中,然后在 __webpack_require__.f.prefetch 查找 map 找到就会通过 link rel prefetch 的方式插入到 head 让浏览器处理。

重复提示一下:异步模块里面依赖了异步模块并且使用了 webpackPrefetch 才会被加入到 chunkToChildrenMap,比如 index.js 如下 image.png

XX.js 如下 image.png

async.js
(忽略)

这个时候会在 runtime 产生以下代码:

/* webpack/runtime/chunk prefetch trigger */
(() => {
var chunkToChildrenMap = {
  XX: ['async'],
};
__webpack_require__.f.prefetch = (chunkId, promises) =>
  Promise.all(promises)
    .then(() => {
      var chunks = chunkToChildrenMap[chunkId];
      Array.isArray(chunks) && chunks.map(__webpack_require__.E);
    })
    .catch(() => {});
})();

在 webpack 通过 __webpack_require__.e 处理异步的 XX.js 模块

  1. __webpack_require__.f.j 加载 XX.js 本身
  2. __webpack_require__.f.prefetch 检查 XX.js 内部的 prefetch 模块,找到 async.js 去 prefetch async.js