storage 存储 API
本教程共 56 篇 · 第 34 篇 · 更新于 2026-08-13 · 约 7 分钟阅读
本节目标:学完你能用
chrome.storage在扩展各组件之间安全地读写数据,并感知任意一处的数据变化。
扩展运行时由多个组件拼成:弹出页、选项页、服务工作者、内容脚本各管一摊。它们之间要共享一份设置或缓存,靠的不是页面自己的 localStorage,而是 chrome.storage。这一章我们就把这套存储机制讲透。
34-1 为什么不用 localStorage
很多前端同学第一反应是:localStorage 我熟,直接拿来用。在普通网页里没问题,但在扩展里它有三个绕不开的坑。
第一,服务工作者(后台)里根本没有 window,自然也没有 localStorage。后台是扩展的”大脑”,它读不到数据,整套逻辑就断了一截。
第二,内容脚本虽然能摸到 localStorage,但那存的是宿主网页自己的数据,和扩展的存储完全不搭界——你在扩展里存的东西,内容脚本根本拿不到。
第三,localStorage 归在”浏览数据”里,用户一清浏览数据它就没了。
(注:弹出页和选项页倒是同源的,它们之间的 localStorage 互通——但这改变不了上面三条,所以结论不变:别用。)
chrome.storage 正好是为此设计的:它跨所有组件共享、后台也能用、全部异步返回,而且写进去的数据会持久保存,关掉浏览器再开还在。
Note使用
chrome.storage前,记得在清单里声明"storage"权限,否则调用会直接报错。
34-2 三种存储区:local / sync / managed
chrome.storage 不是一个大箱子,而是分了三个”区”,分别挂在 chrome.storage.local、chrome.storage.sync、chrome.storage.managed 下。它们的行为差异很大,选对区是写好存储的第一步。
local 是存在本机设备上的。它不会同步到其他设备,容量也相对宽裕,适合放体积偏大、或者只在当前电脑有用的数据,比如缓存的网页内容、本地偏好。
sync 会跟随用户的 Google 账号,在登录同一账号的多台 Chrome 设备之间自动同步。你在一台电脑改了设置,另一台自动跟着变。它容量小、有频率限制,最适合放”用户设置”这类轻量数据。
managed 由企业的域名管理员通过策略下发,扩展只能读、不能写。普通个人扩展基本用不到,但要知道它的存在,避免和它重名冲突。
Tip一句话记忆:
local存本机、sync存云端同步、managed存管理员下发。绝大多数个人扩展,在local和sync之间二选一就够了。
34-3 读取数据:get
读数据用 get。它是异步的,可以用回调,也可以用 await 拿到结果对象。
// 读取单个键,返回 { theme: "dark" }
const items = await chrome.storage.local.get("theme");
console.log(items.theme);
// 一次读多个键
const data = await chrome.storage.local.get(["theme", "fontSize"]);
console.log(data.theme, data.fontSize);
// 传入默认值对象:键不存在时返回默认值
const cfg = await chrome.storage.local.get({ theme: "light", fontSize: 14 });
console.log(cfg.theme); // 没存过就输出 "light"
第三个写法特别实用:把默认值直接写进 get 的参数对象里。这样即使对应的键从没存过,返回的也不会是 undefined,逻辑更稳。
如果想把所有数据一把抓回来,可以传 null:
const all = await chrome.storage.local.get(null);
console.log(all);
34-4 写入数据:set
写数据用 set,参数是一个”键到值”的对象。一次可以写多个键,整体作为一个原子操作完成。
await chrome.storage.local.set({ theme: "dark", fontSize: 16 });
值可以是字符串、数字、布尔、数组、普通对象,但不能是函数、DOM 节点、或者带循环引用的结构——这些 JSON 序列化不了,会被悄悄丢弃。
注意一个常见误区:set 是按键覆盖,不是整体替换。你先 set({ a: 1, b: 2 }),再 set({ a: 9 }),结果是 { a: 9, b: 2 },原来的 b 还在。如果想清空重来,得先 clear(见下节)。
34-5 删除与清空:remove / clear
只想删几个键,用 remove:
// 删单个键
await chrome.storage.local.remove("theme");
// 删多个键
await chrome.storage.local.remove(["theme", "fontSize"]);
想一股脑全清空,用 clear:
await chrome.storage.local.clear();
这会把整个存储区清空,且无法撤销,调用前最好跟用户确认一下。下面这个来自官方示例的写法,就是先 remove 单个键来实现”重置”:
async function reset() {
await chrome.storage.local.remove("css");
console.log("已重置存储的 CSS");
}
34-6 监听变化:onChanged
chrome.storage 最香的能力,是数据变了能被”通知到”。任意一个组件改了某个键,其他组件都能通过 onChanged 收到事件,实时联动。
chrome.storage.onChanged.addListener((changes, areaName) => {
console.log("发生变化的存储区:", areaName);
// changes 的结构:{ 键名: { oldValue, newValue } }
for (const [key, { oldValue, newValue }] of Object.entries(changes)) {
console.log(`键 ${key} 从`, oldValue, "变为", newValue);
}
});
// 触发变化后,上面监听器会打印
await chrome.storage.local.set({ theme: "dark" });
changes 是个对象,每个被改动的键对应一个 { oldValue, newValue }。新增的键 oldValue 是 undefined,删除的键 newValue 是 undefined。areaName 告诉你变化发生在 local、sync 还是 managed。
Tip弹出页、选项页、服务工作者里都可以挂
onChanged。比如用户在选项页改了主题,弹出页立刻就能收到事件刷新界面,不用重新打开。
34-7 配额与常见坑
sync 区有硬性配额,别往里硬塞大文件。它单次写入有大小上限,总容量也小(约百 KB 量级),并且对每分钟、每小时的写入次数都有限制。把用户设置这类轻量数据放 sync 是对的,把整页 HTML 缓存放进去就会频繁触顶。
local 区容量宽裕得多,但也不是无限。存大体积数据时,建议先算一下字节量:
const bytes = await chrome.storage.local.getBytesInUse(null);
console.log("当前已用字节数:", bytes);
还有两个坑值得提一句。一是 sync 在没登录账号、或离线时写入可能失败,别假设它一定成功。二是存储走的是 JSON 序列化,函数、类实例的方法都会丢,存之前想清楚”能不能被 JSON 化”。
34-8 数据组织与命名建议
随着扩展功能变多,存进 storage 的键也会越来越多。这里有几个让数据不混乱的习惯。
给键加前缀是个好办法。比如和用户界面相关的放 ui.theme、ui.fontSize,和网络相关的放 net.cache、net.lastSync。一眼就能看出某条数据归谁管,清理时也不容易误删。
尽量扁平存储,别动不动就套深层嵌套对象。比如用户设置,直接平铺成 theme、fontSize、language 几个键,比塞进一个 settings 大对象更方便单独读写和监听。
还有一个细节:onChanged 是按”键”通知的。你平铺存,改 theme 时监听器只收到 theme 的变化;若是都塞进 settings 对象,改其中一项也会触发整个 settings 的变更,区分起来更麻烦。
Tip存储区的选择也有章法:会跨设备跟随用户的轻量设置放
sync,本机独享或体积偏大的放local,管理员下发的放managed。功能上线前先想清楚每条数据属于哪一类,后面省不少返工。
Note这一章我们只讲
chrome.storage主线。它已经覆盖了存储 API 的全部日常用法:选对区、用get/set读写、用remove/clear清理、用onChanged联动。把这套吃透,扩展里”记住用户选择”的需求就不再是问题。
34-9 别忘了 storage.session
除了 local / sync / managed,Chrome 102+ 还有一个第四存储区:storage.session。它专门给服务工作者当”内存缓存”用。
它的特点是:容量约 10 MB,数据只活在当前浏览器会话里,浏览器一关就清空,不落盘、不同步。读写 API 和 local 完全一样:
// 服务工作者里缓存一份抓取结果,省得每次唤醒都重新拉
await chrome.storage.session.set({ lastFetch: { data: "..." } });
const cached = await chrome.storage.session.get("lastFetch");
因为会话区不落盘,适合放”重算成本高、但丢了也无所谓”的临时数据;用户设置这类要长期保存的,还是放 local / sync。默认情况下内容脚本读不到 session 区(需要 setAccessLevel 显式放开),这恰好让它更适合后台自己用。