background 与 Service Worker 声明
本教程共 56 篇 · 第 8 篇 · 更新于 2026-08-13 · 约 6 分钟阅读
本节目标:学完你能用 background.service_worker 给扩展挂上后台脚本,理解它为什么是”服务工作者”而不是常驻页面,以及”只在需要时运行”这件事意味着什么。
扩展除了前台能点的按钮、能弹的页面,还需要一个不在屏幕上、却随时能干活的角色。它负责监听事件、记状态、跑定时任务。在 Manifest V3 里,这个角色叫后台,而它的实现方式是服务工作者(Service Worker)。
这一节讲三件事:background 怎么声明、服务工作者到底是什么、以及”只在需要时运行”带来的写法变化。
8-1 用 service_worker 声明后台
V3 里声明后台极其简单,只要在清单加一个 background 对象,里面写一个 service_worker 指向你的 JS 文件。
{
"manifest_version": 3,
"name": "我的标签整理器",
"version": "1.0.0",
"background": {
"service_worker": "background.js",
"type": "module"
}
}
service_worker 的值是字符串,指向扩展目录里的一个脚本文件。浏览器加载扩展时会注册它,之后这个脚本就作为服务工作者运行。
这里有个硬性限制务必记住:service_worker 只能指向单个文件,不能是一个数组,也不能直接写多个文件。 如果你的后台逻辑拆成了多个模块,得用 import 在一个文件里聚起来,而不是在清单里列一串。
// background.js
import { initAlarm } from "./alarm.js";
import { handleMessage } from "./message.js";
initAlarm();
chrome.runtime.onMessage.addListener(handleMessage);
Note注意:要用
import拆模块,清单里必须同时写上"type": "module"(见上面的示例),否则经典脚本里出现import会直接语法报错、服务工作者注册失败。被引入的文件也得在扩展包内、走相对路径。浏览器只认service_worker指向的那一个入口。
8-2 后台就是服务工作者,没有 page
理解 V3 后台的关键,是先丢掉”后台页面”这个旧念头。V3 里没有后台页面,后台脚本运行在一种叫服务工作者(Service Worker)的环境里。
服务工作者原本是 Web 平台的概念,用来做离线缓存、推送这类事。扩展借用它当后台,带来一个核心特征:它不是常驻内存的,而是事件驱动、按需启动、空闲就停。
对比一下两种心智模型:
- 旧模型:后台像个一直开着的房间,灯常亮,你在里面放着全局变量、定时器,随时有人敲门。
- V3 模型:后台像个随叫随到的临时工。有事件(用户点击、定时闹钟、消息到来)才被叫醒开工;没事干一会儿,浏览器就让它”下班”,相关内存释放掉。
所以写 V3 后台,第一原则就是:别假设脚本一直活着。
// background.js
chrome.runtime.onInstalled.addListener(() => {
console.log("扩展安装或更新了");
});
上面这段代码注册了一个监听器。当用户安装扩展、或版本更新时,浏览器唤醒服务工作者,执行这个回调。干完活,它又可能被休眠。
Tip调试时你会看到服务工作者有”状态”显示,有时是”运行中”,有时是”已停止”。看到”已停止”别慌,那是正常的省资源行为,不是你写错了。
8-3 只在需要时运行意味着什么
“只在需要时运行”不是一句口号,它直接改变你写代码的方式。我列几个最实在的影响。
第一,全局变量不持久。你在一个事件里写 let count = 1,下次事件唤醒时这个 count 可能归零,因为服务工作者是重新启动的,上次的内存丢了。
// 错误思路:指望全局变量跨唤醒保存
let cache = {};
chrome.runtime.onMessage.addListener((msg) => {
cache[msg.key] = msg.value; // 唤醒后 cache 可能是空的
});
正确做法是把要跨唤醒保存的状态写进 chrome.storage,它独立于服务工作者生命周期。
// 正确思路:状态交给 storage
chrome.runtime.onMessage.addListener((msg) => {
chrome.storage.local.get("cache", (data) => {
const cache = data.cache || {};
cache[msg.key] = msg.value;
chrome.storage.local.set({ cache });
});
});
第二,定时器要换成 chrome.alarms。普通的 setInterval、setTimeout 在服务工作者休眠后不会按时响。V3 改用 chrome.alarms 来排定时任务,它能在指定时间把服务工作者唤醒。
chrome.alarms.create("refresh", { periodInMinutes: 30 });
chrome.alarms.onAlarm.addListener((alarm) => {
if (alarm.name === "refresh") {
console.log("每半小时被叫醒一次");
}
});
第三,监听器必须在一加载就注册。因为服务工作者是被事件唤醒的,它必须先”登记”自己关心哪些事件,浏览器才知道该在什么时候叫醒它。所以监听代码要写在脚本顶层,不要包在某个函数里等调用才注册。
Note把监听器写在顶层、状态存进 storage、定时用 alarms—这三条是 V3 后台能稳定跑起来的地基。后面讲服务工作者注意事项的章节会再展开,这里先建立直觉。
8-4 一个最小后台示例
结合前面的 action,一份带后台的清单长这样:
{
"manifest_version": 3,
"name": "我的标签整理器",
"version": "1.0.0",
"description": "一键把杂乱的标签页按域名分组。",
"action": {
"default_popup": "popup/popup.html",
"default_title": "点击整理标签页"
},
"background": {
"service_worker": "background.js"
}
}
对应的 background.js 哪怕只放一行注册,也是合法后台:
chrome.runtime.onInstalled.addListener(() => {
console.log("后台已就绪,等着被事件叫醒");
});
目录结构大致是:
extension/
manifest.json
background.js
popup/
popup.html
popup.js
Tip想确认后台是否正常工作,去
chrome://extensions打开”开发者模式”,找到你的扩展,点”Service Worker”后面的蓝色链接,就能看到后台的console和当前状态。这一节先知道有这个入口,详细调试后面章节再讲。
8-5 为什么要这样设计
你可能想问:让后台一直活着不是更简单吗?确实,旧模型写起来直观,但代价是占内存、耗电、拖慢浏览器。现代浏览器追求”不用就不占”,服务工作者正是这套思路的体现—扩展不做事时,几乎零开销。
对开发者来说,代价是写法要适应”非持久”。把状态外置、监听器提前注册、定时走 alarms,习惯了之后代码反而更干净,也更符合事件驱动的本色。
Note一句话就够:V3 的后台是“被事件叫醒的临时工”,不是”常驻的房间”。
background.service_worker是你雇它的唯一入口,而怎么让它高效干活,靠的是把状态放进 storage、把监听写在顶层、把定时交给 alarms。
到这一节,action 给了用户入口,background 给了扩展大脑。再往后我们会正式讲内容脚本怎么注入网页、权限怎么声明,扩展的能力版图就完整铺开了。