你以为 import() 失败后只能等用户刷新?Chrome 156 今天把模块加载失败重试彻底接上了
网络抖一下、CDN 抖一下、部署时 static 目录还没 ready——这个时候 import() 会失败,然后你刷新页面,它又失败了,因为浏览器把失败结果缓存了,第二次 import() 直接走缓存的失败结果,根本不发网络请求。这是一个存在了很久的浏览器行为,官方说法是”模块加载失败会被缓存是设计如此”。Chrome 156 改了这件事:失败后开发者可以主动发起重试。
WHATWG HTML 规范里对这个行为有完整定义:当浏览器尝试加载一个 ES module 并失败时,这个失败会被记录在 module map 里,同一个 specifier 的后续 import() 调用会直接返回那个缓存的失败结果,不会重新发请求。这个设计在大部分时候是合理的——模块不存在就真的不存在,没必要反复请求。但它的代价是:网络抖动导致的暂时性失败也被永久缓存了,用户只能等浏览器重启或者清除缓存才能恢复。
Chrome 156 对应的是 WHATWG HTML 规范的变更(PR #10327 已合并),改动方向是:让缓存的失败结果可以被主动 invalidate,给开发者提供一个手动重试的窗口。具体实现是调用 import() 重新请求同一个 URL,浏览器会重新发起网络请求并更新 module map 里的状态。这个改动不改变规范里”失败结果会被缓存”的设计,只是给了一个显式绕过缓存的路径。
对于线上生产环境,这个改动最直接的价值是改善弱网用户的体验。在地铁里、信号不好的会议室、或者企业内网限速场景下,用户第一次访问可能因为 CDN 抖动导致模块加载失败,之前的做法是让用户手动刷新或者清除缓存,现在可以在 UI 层给一个”重试”按钮,主动重新加载失败的模块。
const tryLoadModule = async (specifier) => {
try {
return await import(specifier);
} catch (err) {
// 第一次失败,在这里给用户一个重试选项
return { retry: () => import(specifier) };
}
};
// 用户点击重试按钮时,第二次 import() 会真正发网络请求
// 而不是直接返回缓存的失败结果
目前这个行为的最终规范细节还在 Chromium 实现过程中,是否作为公开 API 暴露还是仅作为内部行为改进,各浏览器可能会有不同实现路径,建议关注 Chrome Platform Status 的更新。写前端的人都踩过这个坑——弱网环境下用户第一次没加载成功,后续刷新也没用,浏览器把失败结果当成真理缓存起来了。Chrome 156 今天把这个重试路径还给你了。
评论区
登录后可评论。