首页 / WXT 浏览器扩展框架教程 / Background:扩展的后台大脑

WXT 浏览器扩展框架教程

Background:扩展的后台大脑

本教程共 45 篇 · 第 10 篇 · 更新于 2026-08-13 · 约 3 分钟阅读

WXTbackground后台service-worker浏览器扩展状态管理

本节目标:认识后台入口与 defineBackground,理解 MV3 service worker 与 MV2 后台页的差异,掌握「薄入口」原则和 worker 休眠带来的内存陷阱。

后台是什么

background 是扩展的后台大脑:扩展一启动它就在,负责集中监听浏览器事件、协调各环境的消息(消息通信见 §17)、处理需要长期运行的任务。

WXT 里创建它:

// entrypoints/background.ts
export default defineBackground(() => {
  // 后台加载时执行
});

也可以带配置对象:

export default defineBackground({
  type: 'module',        // 使用 ES module
  persistent: false,     // MV2 下是否常驻
  main() {
    // 后台加载时执行,不能是 async
  },
});

MV3 的 service worker 与 MV2 的后台页

后台入口在两种 manifest 版本下形态不同:

  • MV3:编译成 service worker。事件驱动,空闲就睡,浏览器自动管理;
  • MV2:编译成后台页(background page)。常驻内存,一直活着。

WXT 按目标版本自动生成对应配置,你通常不用操心。但两种形态的行为差异直接决定代码写法,下面三条规则请记牢。

规则一:main 不能是 async

WXT 构建时会在 Node 环境导入入口文件来读取配置。因此入口文件顶层的运行时代码一律不行——包括顶层调用浏览器 API、顶层操作 DOM。所有运行时逻辑必须放进 main(内容脚本、未列出脚本同理,原理如 §06 所述):

// ❌ 顶层就调用浏览器 API
browser.action.onClicked.addListener(() => {});

// ✅ 放进 main
export default defineBackground(() => {
  browser.action.onClicked.addListener(() => {});
});

main 本身也不能标记 async。需要异步时,在 main 内部再开异步函数。

规则二:薄入口,只注册监听

后台最常见的坏味道:main 里塞几百行业务逻辑。正确姿势是 main 只做「注册」,逻辑抽到 lib 模块。看 mkext 的后台入口,整个 main 只有两行消息监听:

// entrypoints/background/index.ts(简化)
export default defineBackground(() => {
  onMessage(Message.USER, () => userStore.getValue());
  onMessage(Message.DOMAIN_RATINGS, ({ data: domains }) =>
    readRatings(domains),
  );
});

真正的请求合并、缓存、超时处理都在 readRatings 等函数里。main 薄,worker 重启快,逻辑也好测。

规则三:worker 会休眠,内存不持久

MV3 的 service worker 空闲约 30 秒没有事件,就会被浏览器终止(联网补充:https://mv3-extension.com/manifest-v3-architecture-extension-lifecycle/service-worker-fundamentals )。醒来后一切从头开始——内存里的变量全部丢失

后果很直接:别把状态只放在后台内存里。要么用 chrome.storage 这类持久存储(如 §18 所述),要么接受「缓存丢失、重新计算」的事实。

mkext 在代码评审中专门记过两条教训:

  • 内存缓存必须设上限。Firefox MV2 后台页常驻,缓存不清会无限增长,最后给缓存加了 MAX_CACHE_ENTRIES = 500 做 FIFO 淘汰;
  • 请求超时、并发、失败处理都要写进后台逻辑,不能依赖 worker 常驻兜底。

写缓存、会话、计数这类状态前,先问一句:worker 睡一觉回来,这数据还在吗?不在,就放存储。

最小后台示例

一个只监听安装事件的最小后台:

export default defineBackground(() => {
  browser.runtime.onInstalled.addListener((details) => {
    if (details.reason === 'install') {
      console.log('首次安装');
    }
  });
});

配合 §08 的「无弹窗 action」,browser.action.onClicked 的监听也写在这里:点图标、查状态、开页面,都从后台统一调度。

什么时候不该用后台

后台不是越重越好。能在内容脚本或页面里完成的事,别绕到后台:每多一次跨环境通信,就多一层序列化和出错面。后台适合的是「全局职责」——事件监听、跨页面协调、需要常驻的缓存。判断标准很简单:这件事是不是整个扩展都依赖的?不是,就放回离它最近的环境。

小结

  • 后台是扩展的调度中心:薄入口只注册监听,业务逻辑抽到 lib。
  • 浏览器 API 只能放进 main,顶层调用会报错(原理见 §06)。
  • MV3 的 worker 会休眠,长期状态进 storage(§18),别指望内存。