Why
从 sentry 报错中, 会偶然遇到错误
Failed to fetch dynamically imported module: xxx.js
Unable to preload CSS for xxxx.css
报错信息是 chrome 的, 不同浏览器报错的具体文案可能不一样. 一般是网络不稳定导致
Analysis
- 报错 1 是动态导入, 一般源于我们工程化过程中的按需加载失败的场景
- 报错 2 可能涵盖的场景更普遍, 就是 dom 里
inline
某些资源,比如src
中的资源下载失败
Solution
inline
资源 fallback 方案
Url Fallback 这个包,可以配置备用地址, 当监听到 dom 中加载的 js/css/img 发生加载失败的情况后, 会启用备用地址加载对应资源.
我们可以将额外配置出多个备用 cdn, 比如主 img.cdn.com, 备用 img1.cdn.com ,img2.cdn.com.
通过重试来降低失败概率
动态加载失败, 无法重试
https://github.com/whatwg/html/issues/6768 html 的标准中对动态加载有 cache. 也就是当我们第一次动态加载失败后, 第二次及时网络或各方面都恢复了, 但依然会得到 cache 中失败的结果.无法真正去重试获取资源. 这个问题是出在很根本的上不支持. 所以我们在工程化的级别上是无法处理的. 迫使大部分是不得不当监听到错误后,给用户提示,引导用户刷新页面来去除缓存.
借助 prefetch
之前在 vue2 的时代, 我们都是用的 vue-cli 来开启新项目. vue-cli 中底层依赖 webpack 来打包,默认开启了 prefetch 能力, 也就是将各种打包出来的资源(包括动态加载的资源),在 index.html 中做 prefetch 处理.
这样我们就能让动态资源也能使用
inline
的 fallback 方案
新问题
vue 3 + vite 的组合. vite 生态中没有 prefetch 插件. 这成了方案实现的卡点.
- 方案 1: 写一个 nodejs 脚本, 扫描生成的资源文件, 然后读取
index.html
并插入到head
里适当的位置比较定制化, 需要读取 env 中的 baseURL 路径. 需要额外的包来处理 html 的解析和修改. 并且不能保留打包过程的原子化, 和打包过程是分离的
- 方案 2: 开发 prefetch 插件.
配置比较方便,代码统一. 和打包流程无缝整合. 可能需要兼容多个 vite 版本. 需要学习 vite 插件相关知识
结果
起初, 我对方案 2 有点没信心, 以前也没有接触过 webpack 的插件. 而且觉得大概率需要处理ast
, 相对来说复杂度可能比较高,担心自己搞不定.
当想通方案 1 后, 就有了托底方案. 经过 1 天看插件文档后, 实现了插件. 幸运的是这个插件的过程不需要操作ast
.
https://github.com/dreambo8563/vite-plugin-bundle-prefetch , 效果符合预期
这样我们就对 静态, 动态资源都有重试方案了. prefetch
也能提高后续页面的加载速度.