静态资源容错性处理方案

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 也能提高后续页面的加载速度.