ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

@wordpress/data 数据插件与持久化(Persistence)插件完整指南:将 Store 状态持久化到 localStorage 的实战方案

@wordpress/data 数据插件与持久化(Persistence)插件完整指南:将 Store 状态持久化到 localStorage 的实战方案 wordpress/data 数据插件与持久化Persistence插件完整指南将 Store 状态持久化到 localStorage 的实战方案【免费下载链接】gutenbergThe Block Editor project for WordPress and beyond. Plugin is available from the official repository.项目地址: https://gitcode.com/GitHub_Trending/gu/gutenberg本指南以 packages/data/src/plugins/README.md 及其核心插件 packages/data/src/plugins/persistence/README.md 为主体深入讲解 WordPress Gutenberg 数据模块wordpress/data的插件机制以及默认随包提供的持久化Persistence插件的完整使用方式、配置选项与底层实现原理。读完本文你将掌握如何在自定义 store 中按需持久化全部或部分 state、如何自定义存储后端与存储键以及持久化在刷新页面后如何被还原为 initialState。什么是 Data 插件Data Pluginswordpress/data是 WordPress 的数据状态管理模块内置一套基于 Redux 的注册表registry体系用于管理插件与 WordPress 自身的应用状态。packages/data/src/plugins/目录下存放的是该模块默认提供的一组插件集成——目前仓库中实际落地的是persistence插件其导出在 packages/data/src/plugins/index.ts 中export { default as persistence } from ./persistence;从概念上讲Data 插件是一个用于扩展注册表行为的对象。在 packages/data/src/registry.ts 的use方法L342 附近中可以看到插件会被合并merge到注册表从而覆盖或增强registerStore等注册表方法。持久化插件正是通过重写registerStore在 store 注册时注入从存储还原初始状态和订阅 state 变化并写入存储两段逻辑。如何安装wordpress/data是独立 npm 包可单独安装使用npm install wordpress/data --save该包假定运行环境为ES2015。若目标环境对语言特性或 API 支持有限需按 packages/babel-preset-default 提供的 polyfill 说明进行补充。启用插件use 方法对于插件目录中包含的任何一个插件都可以调用注册表的use方法将其行为接入注册表。默认全局注册表可通过以下两种等价方式启用// npm 用法 import { plugins, use } from wordpress/data; use( plugins.persistence ); // WordPress 全局对象用法wp.data wp.data.use( wp.data.plugins.persistence );持久化插件支持带选项的启用方式例如自定义存储键wp.data.use( wp.data.plugins.persistence, { storageKey: example } );如果你是自行通过createRegistry()创建的自定义注册表同样可以调用它的use方法——这在 packages/data/src/plugins/persistence/test/index.js 的测试初始化中即为此用法registry createRegistry().use( plugin, { storage: objectStorage } );注意use方法在仓库源码中已被标记为计划弃用registry.ts 中有// TODO: Remove this after use() is removed.注释并计划以registerGenericStore相关能力取代但当前仓库中持久化插件的标准接入方式仍然是use。让 Store 参与持久化persist 配置项启用插件后在注册 store 时设置persist属性即可让该 store 参与持久化。persist有两种取值true持久化该 store 的全部state字符串数组如[preferences]只持久化 state 中指定的若干 key。wp.data.registerStore( my-plugin, { // ... persist: [ preferences ], } );在插件实现中packages/data/src/plugins/persistence/index.tspersist的完整类型被定义为persist?: boolean | string[]L199。当未传入或为 falsy 时直接透传注册表原生registerStore不做任何持久化处理L202-L204。真实项目中的用法Gutenberg 自身的多个 store 就使用了该机制可作为最佳实践参考packages/block-editor/src/store/index.jspersist: [ preferences ]块编辑器仅持久化用户偏好packages/nux/src/store/index.js同样以persist: [ preferences ]持久化 NUX新手引导偏好。这验证了一个关键实践应优先持久化偏好类的小型、可序列化数据而不是整个大型 store。配置选项Options持久化插件支持两个选项均在persistencePlugin的 options 接口中定义见 persistence/index.ts。storage自定义持久化存储实现。必须至少实现 Web Storage API 中的getItem与setItem两个方法可参考 MDN Storage 文档。通过该选项你可以在测试中使用内存存储、在 Node/SSR 环境中使用自定义存储或接入 IndexedDB 等替代方案。类型定义StorageInterface与插件选项的对应关系为storage?: StorageInterface; // 未提供时默认使用 defaultStorage storageKey?: string; // 未提供时默认 WP_DATAstorageKey持久化数据在存储中使用的键名。默认值为WP_DATA。设置不同的storageKey可以避免与页面中其他使用wordpress/data的应用或多个注册表发生键冲突wp.data.use( wp.data.plugins.persistence, { storageKey: example } );默认存储与优雅降级默认情况下持久化基于localStorage。在localStorage不可用的环境中例如禁用存储的隐私模式、部分 WebView插件会优雅降级到内存对象存储——该存储不会跨会话保留数据。这一降级逻辑在 packages/data/src/plugins/persistence/storage/default.ts 中实现代码尝试写入localStorage的测试键若抛出异常如 Safari 10 及更早版本的隐私浏览模式则回退到objectStorage。内存对象存储在 storage/object.ts 中实现其getItem在键不存在时返回nullsetItem将值转为字符串保存并提供clear()重置整个存储。数据在存储中的格式所有被持久化的 store 状态被统一压缩到单个存储键下格式为 JSON 字符串。结构上以store 名称作为顶层 key各 store 的状态作为其值。这一点从测试断言中可以明确看到// 持久化全部 state 时 registry.dispatch( test ).setState( { ok: true } ); // setItem 被调用为 // WP_DATA, {test:{ok:true}} // 持久化子集 key 时 registry.dispatch( test ).setState( { foo: 1, baz: 2 } ); // persist: [foo] // WP_DATA, {test:{foo:1}}也就是说多个 store 会共享同一个存储键互不覆盖。写入路径在createPersistenceInterface的setData中persistence/index.ts先展开已有数据对象再写入新 key最后整体JSON.stringify后storage.setItem。读取与还原persisted state 如何成为 initialState插件在注册带persist的 store 时会先从存储中读取该 store 的持久化数据persistence.get()[ storeName ]若存在则将其作为initialState注入再调用底层registerStore。核心逻辑位于 persistence/index.ts包含三个值得注意的行为先让 reducer 处理一个特殊的还原 action{ type: WP/PERSISTENCE_RESTORE }得到一个默认初始状态对象与对象之间使用深合并deepmerge以默认 initialState 为基础将持久化值合并进去。合并时仅将纯对象isPlainObject视为可合并对象。这一设计带来两个保障只持久化部分 key 时未持久化的其他 key 仍保留默认值默认 state 中新增的 key 会以默认值作为基底与持久化旧值共存。对象形态不一致时以持久化值为准若默认初始状态是对象而持久化值不是或反之则直接采用持久化值。这些行为都被测试覆盖例如 test/index.js 中的should load a persisted subset value as initialState持久化{ a: 1 }、默认{ a: null, b: null }、persist: [a]还原后得到{ a: 1, b: null }should merge persisted value with default if object-like默认{ preferences: { useFoo: true, useBar: true } }与持久化{ preferences: { useFoo: false } }深合并后为{ useFoo: false, useBar: true }should be reasonably tolerant to a non-object persisted state持久化为数字等非对象值时直接以该值作为 state。另外createPersistenceInterface的getData对异常也有容错存储键不存在getItem返回null或存储内容是损坏的 JSONJSON.parse抛错时都会安全地回退为空对象{}不会让整个注册过程崩溃。写回时机与相等性优化store 注册完成后插件通过store.subscribe订阅状态变化并将变化时写回存储的逻辑接入persistence/index.ts。这里的核心优化点在于createPersistOnChange与withLazySameState当persist是字符串数组时插件利用combineReducers构造一个只提取指定 key 的投影 reducer。combineReducers的特性是当所有子 reducer 的返回值与旧值严格相等时返回原对象引用withLazySameState是一个高阶 reducerpersistence/index.ts若 action 的nextState与当前 state严格相等则直接返回旧 state不调用原始 reducer在订阅回调中仅当投影后的状态与上一次记录的lastState引用不相等时才执行写入L184-L192。这一套组合拳实现了状态未变则跳过写入的优化避免无意义的setItem调用。测试should not persist when state matches initial与should not persist an unchanging subset专门验证了这一点dispatch 了一个不改变状态或改变后又被改回的动作setItem不会被调用。测试还验证了persist不会导致状态对象被意外修改should not mutate options中对冻结Object.freeze后的 options 注册也不会抛出异常。完整实战示例综合以上内容一个完整的最小示例将 store 的部分状态持久化到自定义键名下如下import { plugins, use, registerStore } from wordpress/data; // 1. 启用持久化插件并使用自定义存储键 use( plugins.persistence, { storageKey: my-app-preferences } ); // 2. 注册 store并声明只持久化 preferences 这一部分状态 registerStore( my-plugin, { reducer( state { preferences: { theme: light, density: 0 } }, action ) { switch ( action.type ) { case SET_THEME: return { ...state, preferences: { ...state.preferences, theme: action.theme } }; case SET_DENSITY: return { ...state, preferences: { ...state.preferences, density: action.density } }; default: return state; } }, actions: { setTheme( theme ) { return { type: SET_THEME, theme }; }, setDensity( density ) { return { type: SET_DENSITY, density }; }, }, selectors: { getPreferences( state ) { return state.preferences; }, }, persist: [ preferences ], } );此后每次dispatch( my-plugin ).setTheme( dark )都会把{ my-plugin: { preferences: { theme: dark, ... } } }写入localStorage的my-app-preferences键页面刷新后插件会读取该键把preferences深合并进默认 initialState实现偏好自动还原若localStorage不可用如隐私模式则自动降级为内存存储应用仍可正常运行只是刷新后不保留。若你使用 npm 包方式且需要自定义存储后端也可为use传入storage选项只要该对象实现了getItem与setItemimport { plugins, use } from wordpress/data; const myStorage { getItem( key ) { return sessionStorage.getItem( key ); }, setItem( key, value ) { sessionStorage.setItem( key, value ); }, }; use( plugins.persistence, { storage: myStorage, storageKey: my-app } );小结Data 插件机制让wordpress/data的注册表行为可以按需扩展而默认的持久化插件提供了一套开箱即用的状态持久化方案以use启用、以persist声明范围、以storage/storageKey定制后端与键名、以WP_DATA单键 JSON 格式集中存储。其底层实现还包含了优雅降级localStorage → 内存对象、JSON 容错、深合并还原 initialState以及基于引用相等性的写回优化等细节均有对应源码与单元测试packages/data/src/plugins/persistence/test/index.js作为依据。对块编辑器这类需要记忆用户偏好的应用来说这正是小而精的持久化范例。【免费下载链接】gutenbergThe Block Editor project for WordPress and beyond. Plugin is available from the official repository.项目地址: https://gitcode.com/GitHub_Trending/gu/gutenberg创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表