框架
存储

存储 API

See LicenseNPM InstallFollow PlasmoHQ on TwitterWatch our Live DEMO every FridayJoin our Discord for support and chat about our projects

@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/secureSecureStorage API
@plasmohq/storage/hookReact Hook Storage API

使用示例

存储

基础 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 监听更改:

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 (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 设置了一个静态初始值:

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

如果我们在 content.tsx 中订阅此键,我们会看到它是 undefined,直到调用 setHailingFrequency 并传入一个定义的值:

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

如果我们在 options.tsx 中订阅此键,但使用不同的静态初始值,我们将看到该值:

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

在上述设置中,假设我们在任何实例中调用 setHailingFrequency("8472"),我们将看到所有实例现在都显示 "8472" 并开始跟踪存储中的值而不是初始值。

渲染并持久化初始值

通过使用函数而不是静态值,初始值将被持久化到存储内存中。初始化函数有一个参数,即存储中现有的值。如果没有值,则为 undefined

假设我们有一个 popup.tsx,它在存储中没有任何值时将状态初始化为 "42":

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

然后,如果我们在一个新的 Hook 实例中创建我们的 content.tsxoptions.tsx,我们将看到持久化的初始值,而无需调用 setHailingFrequency

content.tsx
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 不能用于多个扩展(无论清单版本如何)。