跳转到主要内容
Manifest V3 的 service worker 经常冷启动:浏览器会在大约 30 秒没活动后把它杀掉,并在下一次事件到来时再重启它。你在 worker 顶部 import 的每一个字节,都会在每次唤醒时被重新解析一次。如果你在 worker 入口里打包了解析器、wasm 运行时或某个 AI SDK,那每次 alarm、消息和点击都得为此买单。

动态 import() 在哪些场景可用

先抛开打包工具不谈,平台层面的规则如下:
场景运行时支持动态 import()
Service worker(background)否,规范不允许
扩展页面(popup、options、新标签页、side panel)
Offscreen 文档
Content scripts是,但需要多一步处理
最让人意外的就是 service worker 的限制:在平台层面,所有浏览器的 service worker 里都不支持动态 import()。worker 顶部的静态 import 语句是没问题的(Extension.js 会把它们打进 bundle),但你没法把某个 chunk 的加载推迟到事件触发时再做。

模式 1:按界面拆分,而不是按 chunk 拆分

service worker 应该负责调度,而不是亲自做计算。让它的静态 import 只包含粘合代码(路由消息、注册监听器),并把所有重活搬到能用按需加载的地方:
  • 跟 UI 紧密相关的工作放到真正需要它的页面里。一个用来格式化代码的 popup,应该在 popup 里 import 格式化器,而不是在 worker 里。
  • 只有 background 才会做的重型工作(解析、编码、推理)放到 offscreen 文档里,按需创建、用完即关。worker 保持成一个轻量的消息路由器。
background.ts
chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
  if (message.type === "parse-feed") {
    ensureOffscreen()
      .then(() =>
        chrome.runtime.sendMessage({ target: "offscreen", ...message }),
      )
      .then(sendResponse);
    return true;
  }
});
之后 offscreen 页面在第一次被请求时再去 import() 那个重型库,开销就摊到了每个文档的生命周期里一次,而不是每次 worker 唤醒都来一次。

模式 2:在扩展页面里按需 import

在任何扩展页面里,普通的动态 import 都能用,打包工具会自动把 chunk 拆出来:
popup.ts
button.addEventListener("click", async () => {
  const { highlight } = await import("./heavy/highlighter");
  output.innerHTML = highlight(input.value);
});
这里没有任何扩展特有的处理:chunk 会被产出到你的构建输出里,并从扩展自身的 origin 加载。

模式 3:在 content scripts 里按需 import

Content scripts 是在页面里执行的,所以它们按需加载的 chunk 必须能被那个页面 fetch 到。两步走:
  1. 通过 web_accessible_resources 暴露这些 chunk 的路径。
  2. chrome.runtime.getURL 来 import,让 URL 解析到你的扩展 origin:
content.ts
async function loadAnalyzer() {
  const src = chrome.runtime.getURL("content_scripts/analyzer.js");
  return import(src);
}
对应的 manifest 块见 web_accessible_resources。如果不配置它,import 会以网络错误失败,因为宿主页面无法 fetch 那些没有显式暴露的扩展文件。

哪些东西干脆不要放进 worker

  • 任何跟 DOM 相关的(DOMParser、canvas、音频):worker 根本没有 DOM,请用 offscreen 文档。
  • 几兆大的 wasm:在 offscreen 文档或者一个额外页面里实例化,并把结果缓存到 chrome.storage,这样重启的代价就很低。
  • 一次性工具(导入、迁移、导出):放到 pages/ 下的一个独立页面里,免得它出现在每条启动路径上。

另见