你以为 import() 失败后会自动重试?今天 Chrome 把这件事彻底修好了
你以为 import() 失败后会自动重试?今天 Chrome 把这件事彻底修好了。
import() 动态导入是前端代码分割的标准写法:用户点了某个按钮,才动态加载那个功能的代码。
但有一个藏在规范里的问题很多人不知道:如果用户的网络在第一次 import() 执行时恰好抖了一下——CDN 抖动、部署切换、或者临时的 DNS 短暂不可达——这个失败会被浏览器的模块映射(module map)永久缓存起来。之后用户再触发同样的动态导入,import() 会立即抛出一个被缓存的失败,而不是重新发起网络请求。换句话说,一次短暂的抖动,就能让整个功能模块在本次会话内彻底坏掉,用户必须关掉页面重开才能恢复。
这不是 bug,这是 HTML 规范原本的设计选择。
Chrome 155 把这件事修掉了。
WHATWG HTML 规范在 2026 年 7 月通过了一项修改(PR #10327,「Don’t cache HTTP errors in the module map」):浏览器不再将 HTTP 错误缓存在模块映射里。这意味着 import() 失败后,再次调用 import() 会真正发起新的网络请求,而不是返回缓存的错误。Firefox 和 Safari 在此之前已经按照这个行为实现,只有 Chrome 直到 154/155 才完成修复。Shopify 的多个团队是推动这个改动的主要力量——他们在生产环境中真实遭遇了这个问题,每次部署切换都会导致部分用户遇到「刷新也不行、重开也不行」的模块加载失败。
实际开发中,这意味着你可以给自己的动态导入加一个标准的重试逻辑:
async function loadWithRetry(path, retries = 2, delay = 300) {
for (let i = 0; i < retries; i++) {
try {
return await import(path);
} catch (err) {
if (i === retries - 1) throw err;
await new Promise(r => setTimeout(r, delay * (i + 1)));
}
}
}
const { handleClick } = await loadWithRetry('./feature.js');
handleClick();
Chrome 155 之前,这段重试逻辑毫无作用——第二次 import() 会直接返回缓存的失败,根本不发出网络请求。现在重试真正生效了。
不过要注意,这个改动只对「真的值得重试」的失败有意义。建议的判断逻辑:
适合重试:网络抖动、CDN 临时错误、移动网络切换——这类问题通常会自行恢复。
不适合重试:404(文件不存在)、语法错误、CSP 阻止——这类问题不会因为重试而自动消失,应该优先修复部署本身。
import() 的错误对象不会告诉你失败的具体原因,需要结合你的网络监控或 CDN 状态来判断是否值得重试,而不是看到任何错误都盲目重试。
下一步:检查你项目里所有动态 import() 的使用点——是否有针对网络抖动的重试逻辑?如果没有,加上有限次重试,这是对用户网络状况最值得投入的健壮性改进。
参考资料
- Chrome Platform Status: https://chromestatus.com/feature/5214647044145152
- WHATWG HTML PR #10327: https://github.com/whatwg/html/pull/10327
- Chrome 155 Beta: https://developer.chrome.com/blog/chrome-155-beta
评论区
登录后可评论。