Skip to main content

Storage API

Plasmo 提供了一个 封装好的 Storage 包 : @plasmohq/storage

这是一个适用于浏览器扩展的抽象了持久化存储能力的应用包管理器。

当 浏览器扩展的 storage API 不可用时,将降级为 localStorage

tip

storage 允许 各个模块之间的状态同步,包括 扩展页面、background 存储、content script 、web pages。

使用这个类库,最好开启 storage 权限。

安装相关模块

pnpm install @plasmohq/storage

这个包,主要导出以下的三个内容

ModulesDescription
@plasmohq/storageThe base Storage API
@plasmohq/storage/secureThe SecureStorage API
@plasmohq/storage/hookThe React Hook Storage API

使用实例

  1. with-storage 是一个使用 storage 同步 options 和 popups 之间的数据。
  2. with-redux 使用 storage 作为 Redux 的存储库的实例。
  3. MICE 这个实例将存储和 webrtc 的 pipe message 结合,通过扩展同步数据。

base Storage API

storage 提供了了简单易用的 API。他可以应用在 Plasmo 的各个模块的 runtime 上下文。 你不需要使用 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" }

自定义存储位置

初始化 Storage 对象的时候,可以提供 area 参数,默认为 sync

const storage = new Storage({
area: "local",
});

提供的选项列表 "sync" | "local" | "managed" | "session"

数据自动 copy

const storage = new Storage({
copiedKeyList: ["shield-modulation"],
});

通过设置 copiedKeyList , 你可以在 content scriptextention pages 中将数据复制到 localStorage。 以上代码会将 shield-modulation ,复制到 localStorage

数据监控 (应用在 state 状态同步)

background.ts
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演示了这个功能。

安全存储

SecureStorage API 通过数据加密解密扩展了 storage,用于敏感数据的存储。

因为 他利用了 Web Crypto SubtleCrypto API , 所以,他只能在 https 的上下文中执行。

import { SecureStorage } from "@plasmohq/storage/secure";

const storage = new SecureStorage();

await storage.setPassword("roosevelt"); // The only diff

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

hook API 是为了在扩展各个模块之间状态同步设计的。 他有好多种不同的用途,但是,最首要也是重要的是在 React 组件中使用。

import { useStorage } from "@plasmohq/storage/hook";

渲染 state 的最新 value

const [hailingFrequency] = useStorage("hailing")
...
{hailingFrequency}

使用自定义存储实例

import { Storage } from "@plasmohq/storage"
import { useStorage } from "@plasmohq/storage/hook"
...
const [hailingFrequency] = useStorage({
key: "hailing",
instance: new Storage({
area: "local"
})
})

渲染 初始值 (无关持久化)

由于 useStorage 的初始化,可能存在于各个组件中,存在着先后顺序。 那么,初始化的时候,可能发生冲突。 Plasmo 定义了初始化机制。 提供固定的原始值,各个组件中的 storage 在未初始化的时候,互相隔离,即互不影响。 但是,一旦其中的任何一个 storage 数据变更,将会同步到其他的组件当中。

以下的实例描述了这个现象:

popup 中定义初始值

popup.tsx
const [hailingFrequency, setHailingFrequency] = useStorage("hailing", "42")
...
<input value={hailingFrequency} onChange={(e) =>
setHailingFrequency(e.target.value)
}/> // "42"

以上的实例,最开始的值显示为 42。

content.tsx 中订阅数据更新

content.tsx
const [hailingFrequency] = useStorage("hailing");
return <p>{hailingFrequency}</p>; // undefined

未定义初始值,将显示 undefined。

options.tsx中订阅数据,并提供不一样的初始值。

options.tsx
const [hailingFrequency] = useStorage("hailing", "147");
return <p>{hailingFrequency}</p>; // "147"

定义了不一样的初始值,显示为 147。

最后,当你在 popup 中输入值的时候,该值将通过 storage 同步到各个模块。

渲染 初始值 (并持久化)

初始化 storage 的时候,传递一个函数。该函数提供一个参数,表示当前 storage 中的值。 如果已经被其他的组件初始化或者变更,该值将为最新的值,否则,将为 undefined。

用如下的实例演示:

设置如下的 popup

popup.tsx
const [hailingFrequency, setHailingFrequency] = useStorage("hailing", (v) => v === undefined ? "200": v)
...
{hailingFrequency} // "200"

然后,设置如下的 options

options.tsx
const [hailingFrequency, setHailingFrequency] = useStorage("hailing", (v) => v === undefined ? "100": v)
...
{hailingFrequency} // "100"

以上,会根据 options 或者 popup 打开的先后顺序不同,而确定为不同的值。

  1. 先打开 popup ,值会初始化为 100
  2. 先打开 options , 值会初始化为 200

高级用法

可以使用 useStorage 的不同返回值,确定不同的变更范围。

  • setStoreValue 将会同步数据到其他的组件
  • setRenderValue 只影响当前的状态
  • remove 则是删除 storage 数据

具体实例如下

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>
</>
);

火狐浏览器的用法

火狐浏览器使用 storage 的时候,你可能遇到如下的错误。

warning

Error: The storage API will not work with a temporary addon ID. Please add an explicit addon ID to your manifest. For more information see https://mzl.la/3lPk1aE

解决办法,是火狐浏览器使用 storage 的时候,需要提供一个扩展 ID。

"manifest": {
"browser_specific_settings": {
"gecko": {
"id": "your-id@example.com"
}
}
}

开发过程中, ID 可以随意定义。 发布的时候,你可以使用你的扩展 ID 来代替。