前言
大家好,我是倔强青铜三 。是一名热情的软件工程师,我热衷于分享和传播IT技术,致力于通过我的知识和技能推动技术交流与创新,欢迎关注我,微信公众号:倔强青铜三。欢迎点赞、收藏、关注,一键三连!!!
存储
您可以使用原生 API(请参阅上面的文档),使用 WXT 的内置存储 API,或从 NPM 安装一个包。
替代方案
-
wxt/utils/storage
(推荐):WXT 自带了一个围绕原生存储 API 的封装,简化了常见的使用场景。 -
DIY:如果您正在迁移到 WXT 并且已经有了一个存储封装,可以继续使用它。将来,如果您想删除该代码,可以使用这些替代方案之一,但在迁移过程中没有理由替换正在工作的代码。
-
其他任何 NPM 包:有很多围绕存储 API 的封装,您可以找到喜欢的一个。这里是一些流行的:
webext-storage
- 一个更可用的类型化存储 API,适用于 Web 扩展@webext-core/storage
- 一个类型安全的、类似 localStorage 的封装,围绕 Web 扩展存储 API
WXT 存储 [](#WXT 存储 "#wxt-storage")
扩展存储 API 的简化封装。
安装
使用 WXT [](#使用 WXT "#with-wxt")
此模块已内置在 WXT 中,因此您不需要安装任何东西。
ts
import { storage } from '#imports';
如果您使用自动导入,storage
会自动为您导入,因此您甚至不需要导入它!
不使用 WXT [](#不使用 WXT "#without-wxt")
安装 NPM 包:
sh
npm i @wxt-dev/storage
pnpm add @wxt-dev/storage
yarn add @wxt-dev/storage
bun add @wxt-dev/storage
ts
import { storage } from '@wxt-dev/storage';
存储权限
要使用 @wxt-dev/storage
API,必须在清单中添加 "storage"
权限:
wxt.config.ts
ts
export default defineConfig({
manifest: {
permissions: ['storage'],
},
});
基本用法
所有存储键必须以其存储区域为前缀。
ts
// ❌ 这将抛出错误
await storage.getItem('installDate');
// ✅ 这是正确的
await storage.getItem('local:installDate');
您可以使用 local:
、session:
、sync:
或 managed:
。
如果您使用 TypeScript,可以在大多数方法中添加类型参数以指定键值的预期类型:
ts
await storage.getItem<number>('local:installDate');
await storage.watch<number>(
'local:installDate',
(newInstallDate, oldInstallDate) => {
// ...
},
);
await storage.getMeta<{ v: number }>('local:installDate');
有关可用方法的完整列表,请参见 API 参考。
监听器
要监听存储更改,请使用 storage.watch
函数。它允许您为单个键设置监听器:
ts
const unwatch = storage.watch<number>('local:counter', (newCount, oldCount) => {
console.log('计数已更改:', { newCount, oldCount });
});
要移除监听器,请调用返回的 unwatch
函数:
ts
const unwatch = storage.watch(...);
// 一段时间后...
unwatch();
元数据
@wxt-dev/storage
还支持为键设置元数据,存储在 key + "$"
处。元数据是与键关联的一组属性。它可能是一个版本号、最后修改日期等。
除了版本控制之外,您还需要负责管理字段的元数据:
ts
await Promise.all([
storage.setItem('local:preference', true),
storage.setMeta('local:preference', { lastModified: Date.now() }),
]);
当从多次调用中设置不同属性的元数据时,这些属性会被合并而不是被覆盖:
ts
await storage.setMeta('local:preference', { lastModified: Date.now() });
await storage.setMeta('local:preference', { v: 2 });
await storage.getMeta('local:preference'); // { v: 2, lastModified: 1703690746007 }
您可以移除与键关联的所有元数据,或仅移除特定属性:
ts
// 移除所有属性
await storage.removeMeta('local:preference');
// 仅移除 "lastModified" 属性
await storage.removeMeta('local:preference', 'lastModified');
// 移除多个属性
await storage.removeMeta('local:preference', ['lastModified', 'v']);
定义存储项
一遍又一遍地为同一个键编写键和类型参数可能会很烦人。作为替代方案,您可以使用 storage.defineItem
创建一个"存储项"。
存储项包含与 storage
变量相同的 API,但您可以在一个地方配置其类型、默认值等:
ts
// utils/storage.ts
const showChangelogOnUpdate = storage.defineItem<boolean>(
'local:showChangelogOnUpdate',
{
fallback: true,
},
);
现在,您可以使用创建的存储项上的辅助函数,而不是使用 storage
变量:
ts
await showChangelogOnUpdate.getValue();
await showChangelogOnUpdate.setValue(false);
await showChangelogOnUpdate.removeValue();
const unwatch = showChangelogOnUpdate.watch((newValue) => {
// ...
});
有关可用属性和方法的完整列表,请参见 API 参考。
版本控制
如果期望存储项随着时间增长或变化,您可以为其添加版本控制。在定义项目的第一个版本时,从版本 1 开始。
例如,考虑一个存储项,它存储被扩展忽略的网站列表。
v1
ts
type IgnoredWebsiteV1 = string;
export const ignoredWebsites = storage.defineItem<IgnoredWebsiteV1[]>(
'local:ignoredWebsites',
{
fallback: [],
version: 1,
},
);
v2
ts
import { nanoid } from 'nanoid';
type IgnoredWebsiteV1 = string;
interface IgnoredWebsiteV2 {
id: string;
website: string;
}
export const ignoredWebsites = storage.defineItem<IgnoredWebsiteV1[]>(
export const ignoredWebsites = storage.defineItem<IgnoredWebsiteV2[]>(
'local:ignoredWebsites',
{
fallback: [],
version: 1,
version: 2,
migrations: {
// 从 v1 迁移到 v2 时运行
2: (websites: IgnoredWebsiteV1[]): IgnoredWebsiteV2[] => {
return websites.map((website) => ({ id: nanoid(), website }));
},
},
},
);
v3
ts
import { nanoid } from 'nanoid';
type IgnoredWebsiteV1 = string;
interface IgnoredWebsiteV2 {
id: string;
website: string;
}
interface IgnoredWebsiteV3 {
id: string;
website: string;
enabled: boolean;
}
export const ignoredWebsites = storage.defineItem<IgnoredWebsiteV2[]>(
export const ignoredWebsites = storage.defineItem<IgnoredWebsiteV3[]>(
'local:ignoredWebsites',
{
fallback: [],
version: 2,
version: 3,
migrations: {
// 从 v1 迁移到 v2 时运行
2: (websites: IgnoredWebsiteV1[]): IgnoredWebsiteV2[] => {
return websites.map((website) => ({ id: nanoid(), website }));
},
// 从 v2 迁移到 v3 时运行
3: (websites: IgnoredWebsiteV2[]): IgnoredWebsiteV3[] => {
return websites.map((website) => ({ ...website, enabled: true }));
},
},
},
);
INFO
内部使用一个名为 v
的元数据属性来跟踪值的当前版本。
在这种情况下,我们认为忽略的网站列表将来可能会发生变化,并能够从一开始就设置一个版本化的存储项。
实际上,直到需要更改其模式时,您才会知道某个项需要版本控制。幸运的是,向未版本化的存储项添加版本控制非常简单。
当找不到先前版本时,WXT 假定版本为 1
。这意味着您只需设置 version: 2
并为 2
添加迁移,它就会正常工作!
让我们再看一下之前的忽略网站示例,但这次从一个未版本化的项开始:
未版本化
ts
export const ignoredWebsites = storage.defineItem<string[]>(
'local:ignoredWebsites',
{
fallback: [],
},
);
v2
ts
import { nanoid } from 'nanoid';
// 回溯性地为第一个版本添加类型
type IgnoredWebsiteV1 = string;
interface IgnoredWebsiteV2 {
id: string;
website: string;
}
export const ignoredWebsites = storage.defineItem<string[]>(
export const ignoredWebsites = storage.defineItem<IgnoredWebsiteV2[]>(
'local:ignoredWebsites',
{
fallback: [],
version: 2,
migrations: {
// 从 v1 迁移到 v2 时运行
2: (websites: IgnoredWebsiteV1[]): IgnoredWebsiteV2[] => {
return websites.map((website) => ({ id: nanoid(), website }));
},
},
},
);
运行迁移
一旦调用 storage.defineItem
,WXT 就会检查是否需要运行迁移,如果需要,则运行它们。对存储项的值或元数据进行获取或更新的调用(如 getValue
、setValue
、removeValue
、getMeta
等)会自动等待迁移过程完成后再实际读取或写入值。
默认值
使用 storage.defineItem
,有多种方式定义默认值:
-
fallback
- 如果值缺失,则从getValue
返回此值而不是null
。此选项非常适合为设置提供默认值:
tsconst theme = storage.defineItem('local:theme', { fallback: 'dark', }); const allowEditing = storage.defineItem('local:allow-editing', { fallback: true, });
-
init
- 如果尚未保存,则初始化并保存一个值到存储中。这对于需要初始化或一次设置的值非常有用:
tsconst userId = storage.defineItem('local:user-id', { init: () => globalThis.crypto.randomUUID(), }); const installDate = storage.defineItem('local:install-date', { init: () => new Date().getTime(), });
值会立即在存储中初始化。
批量操作
当一次性获取或设置多个值时,您可以执行批量操作以通过减少单独的存储调用来提高性能。storage
API 提供了几个用于执行批量操作的方法:
getItems
- 一次性获取多个值。getMetas
- 一次性获取多个项的元数据。setItems
- 一次性设置多个值。setMetas
- 一次性设置多个项的元数据。removeItems
- 一次性移除多个值(可选移除元数据)。
所有这些 API 都支持字符串键和定义的存储项:
ts
const userId = storage.defineItem('local:userId');
await storage.setItems([
{ key: 'local:installDate', value: Date.now() },
{ item: userId, value: generateUserId() },
]);
有关如何使用所有批量 API 的类型和示例,请参见 API 参考。
最后感谢阅读!欢迎关注我,微信公众号 :
倔强青铜三
。欢迎点赞
、收藏
、关注
,一键三连!!!