# 线上好好的,用户换个 WiFi 就白屏了——这个让 Shopify 工程师追了几个月的 bug,今天 Chrome 把它修了

Chrome 156(Stable 2026-10-07)默认启用”避免缓存模块加载失败“——动态 import() 加载失败后不再永久缓存,浏览器会自动重试。这个行为由 WHATWG HTML 规范变更(PR #10327)定义,Firefox 和 Safari 已率先支持。


这个问题有多普遍

这个 bug 不是边缘案例。它在 Shopify 这种日活数百万的电商平台里被真实遇到,多个团队同时受影响。社区为此写了一个完整的 npm 包 @fatso83/retry-dynamic-import,专门解决”模块加载失败后重试无效”的问题——Star 数说明它不是小众问题。

前端开发者最常遇到的场景:

用户在访问你的 SPA 时,你的服务刚好在部署。 用户的浏览器已经缓存了旧版本的模块映射,新模块的 chunk 文件名变了(chunk-abc123 → chunk-def456),旧的 import() 直接拿到 404,然后这个 404 被永久缓存——用户刷新、白屏,必须清缓存才能恢复。

// 触发时机:用户正在使用,网站刚好部署
const About = await import('./routes/About.tsx');
// 如果恰好此时 chunk 文件名变了:
// TypeError: Failed to fetch dynamically imported module
// 更糟糕的是:这个错误被浏览器永久缓存,刷新也无济于事

用户在地铁里信号不好,模块加载超时。 信号恢复后重试,浏览器仍然返回上次缓存的失败结果,用户卡在白屏。


旧行为:失败即终局

在规范修改之前,浏览器的模块加载失败缓存策略是永久性的:

// 用户的网络恰好抖动了一下
try {
  const mod = await import('./widget.js');
} catch (e) {
  // 网络恢复后重试
  const mod = await import('./widget.js'); // 仍然报错,浏览器直接返回缓存的失败
}

这个行为由 WHATWG HTML 规范规定,Chromium/Firefox/Safari 都遵循。开发者只能靠 workaround:

// 开发者自己做的 cache busting
const retryImport = (path) => import(`${path}?t=${Date.now()}`);
// 这在以前是唯一的解法,但现在规范层面修了,浏览器自动重试

新行为:Chrome 156 自动重试

Chrome 156 改变了模块加载器的缓存策略:加载失败不再永久缓存,而是允许重新发起网络请求。

// Chrome 156+:失败后重试会自动发起新的网络请求
const About = await import('./routes/About.tsx');
// 网络抖动导致的暂时失败,现在会被自动重试
// 无需 ?t=timestamp 这类 workaround

跨浏览器状态:

浏览器 支持状态
Firefox ✅ 已支持
Safari ✅ 已支持
Chrome 156+(2026-10-07 Stable)

这个变化是 WHATWG HTML 规范层面的,意味着三大引擎最终会统一行为。


对现有代码的影响

好消息:大多数代码不需要改动。

import() 调用成功的场景完全不受影响。只有之前依赖”失败缓存”做错误隔离的代码需要注意:

// 以前靠失败缓存做错误隔离的代码,现在行为会变
// 假设 widget.js 确实不存在
const loadWidget = async () => {
  try {
    return await import('./widget.js');
  } catch {
    // 旧行为:这里只执行一次,失败后不再重试
    // 新行为:这段代码如果被再次调用,import() 会重新发起请求
    return null;
  }
};

如果你在 catch 里做了幂等处理(比如返回 fallback),这段代码在新浏览器里行为会更积极(多次重试),通常是你想要的。如果不是,需要主动 AbortController 控制重试次数。

需要移除的 workaround:

// 如果你之前用了 @fatso83/retry-dynamic-import 或自己写的 cache busting
// Chrome 156+ 可以考虑移除,因为浏览器已经在规范层面修了
// 但如果要兼容旧浏览器,保留也没问题

为什么这是 Shopify 级别的痛点

Shopify 的 merchant 后台有大量动态加载的模块。merchant 的员工在移动网络或不稳定 WiFi 下访问时,一次短暂的 CDN 抖动会导致整个后台白屏——即使 CDN 恢复后页面仍然坏着。这不是代码问题,是规范定义的问题。

Shopify 工程师向 WHATWG HTML 规范提交了 issue,推动了 PR #10327 的落地。这是开发者驱动的标准改进案例。


下一步

检查你的代码里是否有:

  1. 动态 import() 调用后跟 retry workaround(?t=timestamp 或 @fatso83/retry-dynamic-import)
  2. 依赖”模块失败只报一次”的错误隔离逻辑

Chrome 156 将于 2026-10-07 进入稳定版。如果你的网站用户大量使用 Chrome,可以考虑在新版本发布后移除旧的 retry workaround,简化代码。如果需要兼容旧版浏览器,保持现状即可——渐进增强。

评论区

0 条评论

登录后可评论。

小智·AI工具控 22 阅读