存储 API
@plasmohq/storage
是来自 plasmo (opens in a new tab) 的实用程序库,它抽象了浏览器扩展可用的持久化存储 API。当扩展存储 API 不可用时,它会回退到 localStorage,允许在扩展页面、内容脚本、后台服务工作线程和网页之间同步状态。
如果在 Plasmo 框架 (opens in a new tab) 项目中作为 依赖项 使用此库,它将自动启用
storage
权限。
安装
pnpm install @plasmohq/storage
该包以 ESM 和 CJS 格式导出以下模块:
模块 | 描述 |
---|---|
@plasmohq/storage | 基础 Storage API |
@plasmohq/storage/secure | SecureStorage API |
@plasmohq/storage/hook | React Hook Storage API |
使用示例
- 查看 with-storage (opens in a new tab),了解如何使用此库在选项和弹出窗口之间同步状态。
- 查看 with-redux (opens in a new tab),了解如何将此库用作 Redux 的持久层(对 MV3 至关重要)。
- 查看 MICE (opens in a new tab),了解此库与 WebRTC 集成以通过扩展在浏览器之间传递消息的实验性用例。
存储
基础 Storage API 设计简单易用。它可以在每个扩展运行时使用,例如后台服务工作线程、内容脚本和扩展页面。
无需 JSON.stringify/parse 即可获取/设置数据。只要存储的数据是可序列化的(普通对象或基本类型),就可以存储:
import { Storage } from "@plasmohq/storage"
const storage = new Storage()
await storage.set("key", "value")
const data = await storage.get("key") // "value"
await storage.set("capt", { color: "red" })
const data2 = await storage.get("capt") // { color: "red" }
自定义存储区域
存储区域默认为 "sync",这意味着数据会在用户登录的所有 Chrome 实例中同步。
您可以在 Chrome 存储 API 文档 (opens in a new tab) 中查看 Plasmo 支持的所有其他可能范围。
const storage = new Storage({
area: "local"
})
自动复制数据到 localStorage
const storage = new Storage({
copiedKeyList: ["shield-modulation"]
})
上述代码将在使用内容脚本或扩展页面时将数据复制到 Web localStorage。
监听(用于状态同步)
使用 Storage API 监听更改:
import { Storage } from "@plasmohq/storage"
const storage = new Storage()
await storage.set("serial-number", 47)
await storage.set("make", "plasmo-corp")
storage.watch({
"serial-number": (c) => {
console.log(c.newValue)
},
make: (c) => {
console.log(c.newValue)
}
})
await storage.set("serial-number", 96)
await storage.set("make", "PlasmoHQ")
这可以用作跨扩展通信消息的层。我们在 with-redux (opens in a new tab) 示例中演示了这一点。
注意: 当 Content Script 在 'MAIN' 世界中执行时,Storage API 不可用,因为它失去了对 Chrome 扩展 API 的访问权限。
安全存储
SecureStorage API 扩展了 Storage,为静态冷存储敏感密钥提供数据加密和解密。它利用了仅在安全上下文(HTTPS)中工作的 Web Crypto SubtleCrypto
API (opens in a new tab)。
import { SecureStorage } from "@plasmohq/storage/secure"
const storage = new SecureStorage()
await storage.setPassword("roosevelt") // 唯一的区别
await storage.set("key", "value")
const data = await storage.get("key") // "value"
await storage.set("capt", { color: "red" })
const data2 = await storage.get("capt") // { color: "red" }
React Hook API
Hook API 旨在简化扩展不同部分之间的状态同步工作流。有许多方法可以使用它,但首先您需要将 Hook 导入到您的 React 组件中:
import { useStorage } from "@plasmohq/storage/hook"
监听并渲染值
const [hailingFrequency] = useStorage("hailing")
...
{hailingFrequency}
使用自定义存储实例
import { Storage } from "@plasmohq/storage"
...
const [hailingFrequency] = useStorage({
key: "hailing",
instance: new Storage({
area: "local"
})
})
渲染初始值而不持久化
"持久化"意味着写入内部内存。
如果不持久化值,则只有此特定 Hook 实例会在存储中没有值时渲染给定的初始值。其他实例可以显示 undefined
或指定它们自己的初始值。具体来说:
假设 popup.tsx
设置了一个静态初始值:
const [hailingFrequency, setHailingFrequency] = useStorage("hailing", "42")
...
<input value={hailingFrequency} onChange={(e) =>
setHailingFrequency(e.target.value)
}/> // "42"
如果我们在 content.tsx
中订阅此键,我们会看到它是 undefined
,直到调用 setHailingFrequency
并传入一个定义的值:
const [hailingFrequency] = useStorage("hailing")
return <p>{hailingFrequency}</p> // undefined
如果我们在 options.tsx
中订阅此键,但使用不同的静态初始值,我们将看到该值:
const [hailingFrequency] = useStorage("hailing", "147")
return <p>{hailingFrequency}</p> // "147"
在上述设置中,假设我们在任何实例中调用 setHailingFrequency("8472")
,我们将看到所有实例现在都显示 "8472" 并开始跟踪存储中的值而不是初始值。
渲染并持久化初始值
通过使用函数而不是静态值,初始值将被持久化到存储内存中。初始化函数有一个参数,即存储中现有的值。如果没有值,则为 undefined
。
假设我们有一个 popup.tsx
,它在存储中没有任何值时将状态初始化为 "42":
const [hailingFrequency, setHailingFrequency] = useStorage("hailing", (v) => v === undefined ? "42": v)
...
{hailingFrequency} // "42"
然后,如果我们在一个新的 Hook 实例中创建我们的 content.tsx
或 options.tsx
,我们将看到持久化的初始值,而无需调用 setHailingFrequency
:
const [hailingFrequency] = useStorage("hailing")
return <p>{hailingFrequency}</p> // "42"
高级用法
在处理表单输入或实时输入时,您可能需要以下功能:
const [hailingFrequency, setHailingFrequency, {
setRenderValue,
setStoreValue,
remove
}] = useStorage("hailing")
return <>
<input value={hailingFrequency} onChange={(e) => setRenderValue(e.target.value)}/>
<button onClick={() => setStoreValue()}>
Save
</button>
<button onClick={() => remove()}>
Remove
</button>
</>
Firefox 上的使用
在开发期间使用存储 API 时,您需要在清单中添加一个 Add-on ID,否则会出现以下错误:
错误:存储 API 在临时插件 ID 下无法正常工作。请在清单中添加明确的插件 ID。更多信息请参见 https://mzl.la/3lPk1aE。 (opens in a new tab)
要在清单中添加 Add-on ID,请将其添加到您的 package.json
中:
"manifest": {
"browser_specific_settings": {
"gecko": {
"id": "your-id@example.com"
}
}
}
Add-on ID 的格式在清单版本之间有所不同。
- 清单 V2:
your-id@example.com
- 清单 V3:
{ed7ba470-8e54-465e-825c-99712043e01c}
(任何 UUID)
一旦您的扩展发布,package.json
文件中定义的 Add-on ID 应该会在扩展的开发者页面的 "技术详情 > UUID" 下显示。
注意: 开发期间使用的 Add-on ID(即清单中定义的那个)很可能是 Mozilla 在发布时分配给您的。如果不是,并且为您生成了一个新的 ID,您需要更新 package.json
文件以包含新 ID。
Add-on ID 是唯一的,同一个 ID 不能用于多个扩展(无论清单版本如何)。