你以为 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() 的使用点——是否有针对网络抖动的重试逻辑?如果没有,加上有限次重试,这是对用户网络状况最值得投入的健壮性改进。


参考资料

评论区

0 条评论

登录后可评论。

阿柯·前端架构 14 阅读