Skip to main content
一个扩展有四个存储区域,而且它们不能互相替代。local 保存设备上的数据,sync 在已登录的配置文件之间携带少量偏好,session 只要浏览器在运行就留在内存里,managed 则是只读的策略数据。本页给出每个区域的配额与可读方,说明让 storage.session 远离 content script 的规则,以及区域写满或被限流时打印的控制台行。

保存状态的三种方式,以及为什么 storage 胜出

service worker 里的变量。 Manifest V3 的后台 service worker 会在空闲时停止,并在下一个事件到来时重新启动。你放在内存里的一切都消失了,而且没有任何报错来提醒你。 扩展页面里的 localStorage 或 IndexedDB。 两者都以页面来源为作用域。service worker 根本不能使用 localStorage,而 content script 看到的是宿主页面的存储,不是你的。 一个 chrome.storage 区域。 异步,任何拥有 storage 权限的扩展上下文都能读取,也不受 worker 重启影响。这是扩展状态的默认答案。

四个存储区域

下面的配额是 Chromium 的默认值。 sync 还会限流写入:大约每分钟 120 次、每小时 1,800 次。放在按键处理器里的写入会很快撞到这个上限,所以请做防抖,只写一次。 按数据回答的问题来选择:
  • 功能状态、缓存与持久设置放在 local
  • 用户期望在第二台电脑上看到的少量偏好放在 sync
  • 不该活过浏览器的单次运行协调数据放在 session
  • 企业策略值从 managed 读取,绝不与用户可编辑的设置混在一起。
chrome.storage 不是唯一的选择。IndexedDB 和源私有文件系统(OPFS)可以在扩展页面和 worker 中存放大体积或结构化数据,受浏览器的常规配额约束。在 content script 中,二者都属于宿主页面的源,因此请把这类数据放在扩展自己的上下文里。

Manifest 片段

storage 权限覆盖全部四个区域。unlimitedStorage 提高 local 的上限,managed 需要一个 schema 文件:
一个从存储恢复、而不是信任内存的设置模块:

从 content script 读取 storage.session

storage.session 一开始对不受信任的上下文是关闭的,而 content script 正是这种上下文。从那里读取会抛错,而不是返回一个空对象。请在 content script 发起请求之前,从 service worker 里开放一次这个区域:
当值是敏感的时候,让区域保持关闭。改为通过一条发往 service worker 的消息来完成读取,做法见 消息传递

各浏览器差异

Firefox 实现了全部四个区域。请把配额表里的数字当作 Chromium 的,并在发布前对着 Firefox 构建核对你的功能所依赖的那个上限。 如果你的源码调用 browser.storage,同时也要构建 Chromium 目标,请传入 --polyfill。参见 跨浏览器兼容

你会看到的控制台报错

把你看到的那一行复制去搜索。每一行对应一个原因。 QUOTA_BYTES_PER_ITEM quota exceeded 一个 sync 键下的单个值大于 8 KB。把这个值拆到多个键,或者把该键移到 local QUOTA_BYTES quota exceeded 区域满了:sync 是所有键合计 100 KB,local 是设备上限。存少一点,或者为 local 这种情况加上 unlimitedStorage MAX_WRITE_OPERATIONS_PER_MINUTE quota exceeded 一分钟内的 sync 写入过多,通常是每次按键或每次滚动都写一次。给处理器做防抖,只写一次稳定后的值。 Access to storage is not allowed from this context. content script 在区域仍然仅限受信任上下文时读取了 storage.session。请从 service worker 调用 setAccessLevel,或者把读取放到一条消息之后。 回调风格的调用通过 chrome.runtime.lastError 报告这些错误,而在你去读它之前它是静默的。Promise 形式会 reject,因此在 try 块里 await,你会看到同样的文本作为被捕获的错误。

Extension.js 的做法

Extension.js 不包装也不替换存储 API。它只编译调用这些 API 的代码,因此上面的平台规则就是全部约定。有两个构建行为值得知道:
  • storage.managed_schema 路径会被校验,schema 文件会被产出到输出目录。该路径必须解析到扩展目录内的文件。Extension.js 会在浏览器启动前把指向目录之外的路径标记为加载阻断,因为 Chrome 会因为缺少 managed schema 而拒绝整个扩展。参见 JSON
  • --polyfill 会把 browser.* 桥接到 Chromium 目标上,因此同一份源码可以为两个家族调用 browser.storage
因为后台 service worker 会重启,请写下重要的状态并按需读回。把内存里的副本当作优化,永远不要当作事实来源。后台脚本 讲解了这个重启模型。 在首个版本之前就为你存储的数据形状定好版本,并为重命名的键保留迁移路径。存下来的对象会比写下它的代码活得更久。

参见