ARTICLE DETAIL

资讯详情

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

VueUse useLocalStorage 完全指南:基于 useStorage 的响应式 localStorage 封装

VueUse useLocalStorage 完全指南:基于 useStorage 的响应式 localStorage 封装 前端【免费下载链接】vueuseCollection of essential Vue Composition Utilities for Vue 3项目地址https://gitcode.com/gh_mirrors/vu/vueuse点击查看免费下载useLocalStorage是 VueUse 中用于将 Vue 响应式状态与浏览器localStorage双向绑定的核心工具它是useStorage在window.localStorage上的直接封装见 packages/core/useLocalStorage/index.ts。本文以 useLocalStorage 官方文档 为主体骨架深入结合useStorage的源码实现与测试用例完整讲解其类型签名、序列化机制、跨标签页同步、合并默认值、响应式 key 以及 SSR 注意事项帮助你彻底掌握这一 Vue 3 状态持久化利器。一、useLocalStorage 是什么useLocalStorage创建了一个响应式 ref可直接读写localStorage且数据变更自动持久化、存储变化自动反向同步。它解决了手动调用localStorage.setItem/getItem时状态与存储脱节的痛点让持久化状态和普通 Vue 响应式状态一样使用。官方文档对它的定义非常简洁Reactive LocalStorage.Usage: Please refer touseStorage.这意味着它的全部行为、选项与能力均由 useStorage 官方文档 定义。useLocalStorage只是把第三个存储参数固定绑定为localStorage源码验证了这一设计// packages/core/useLocalStorage/index.ts export function useLocalStorageT extends(string | number | boolean | object | null)( key: MaybeRefOrGetterstring, initialValue: MaybeRefOrGetterT, options: UseStorageOptionsT {}, ): RemovableRefany { const { window defaultWindow } options return useStorage(key, initialValue, window?.localStorage, options) }核心实现只有 3 行从options中取出可配置的window默认取defaultWindow然后把window?.localStorage作为存储源传给useStorage。所以理解 useLocalStorage 的一切就是理解 useStorage。二、基本用法import { useLocalStorage } from vueuse/core // 绑定对象 const state useLocalStorage(my-store, { hello: hi, greeting: Hello }) // 绑定布尔值返回 Refboolean const flag useLocalStorage(my-flag, true) // 绑定数字返回 Refnumber const count useLocalStorage(my-count, 0) // 绑定字符串 const id useLocalStorage(my-id, some-string-id) // 删除存储中的数据写入 null 会触发 removeItem state.value null与useStorage的签名对比如下useStorage 多了第三参数 storageuseStorage(key, defaults, storage, options?) // 第三参数是存储源可传 localStorage / sessionStorage / 自定义 StorageLike useLocalStorage(key, initialValue, options?) // 第三参数固定为 window.localStorage官方演示页packages/core/useStorage/demo.vue展示了真实场景同一 key 创建两个useLocalStorage实例一个用于v-model编辑一个用于实时预览序列化结果两者通过存储事件保持同步script setup langts import { useStorage } from vueuse/core const theDefault { name: Banana, color: Yellow, size: Medium, count: 0, } const state useStorage(vue-use-local-storage, theDefault) const state2 useStorage(vue-use-local-storage, theDefault) // 与 state 自动同步 /script template input v-modelstate.name typetext input v-modelstate.color typetext input v-modelstate.size typetext input v-model.numberstate.count typerange min0 step0.01 max1000 /template三、类型签名与返回值useLocalStorage依据initialValue的类型提供 5 组重载签名与 test/snapshots/tsnapi/vueuse/core/index.snapshot.d.ts 中的公开类型声明一致export declare function useLocalStorage( key: MaybeRefOrGetterstring, initialValue: MaybeRefOrGetterstring, options?: UseStorageOptionsstring, ): RemovableRefstring export declare function useLocalStorage( key: MaybeRefOrGetterstring, initialValue: MaybeRefOrGetterboolean, options?: UseStorageOptionsboolean, ): RemovableRefboolean export declare function useLocalStorage( key: MaybeRefOrGetterstring, initialValue: MaybeRefOrGetternumber, options?: UseStorageOptionsnumber, ): RemovableRefnumber export declare function useLocalStorageT( key: MaybeRefOrGetterstring, initialValue: MaybeRefOrGetterT, options?: UseStorageOptionsT, ): RemovableRefT export declare function useLocalStorageT unknown( key: MaybeRefOrGetterstring, initialValue: MaybeRefOrGetternull, options?: UseStorageOptionsT, ): RemovableRefT关键点key与initialValue均为MaybeRefOrGetter可以传普通值、ref或返回值的 getter 函数返回值是RemovableRefT比普通RefT多了RemovableRef语义——将.value设为null或undefined时会从存储中移除该 key源码write()中v null分支执行storage.removeIteminitialValue为null时的T unknown重载因为无法从null推断数据类型此时必须配合自定义serializer或显式指定StorageSerializers才能获得完整类型详见第五节。四、Options 完整配置useLocalStorage的options类型为UseStorageOptionsT完整定义在 packages/core/useStorage/index.ts选项类型默认值说明deepbooleantrue是否深度监听对象/数组变化深度修改对象属性也会触发写入listenToStorageChangesbooleantrue是否监听storage事件实现多标签页同步writeDefaultsbooleantrue存储中不存在该 key 时是否把默认值写入存储mergeDefaultsboolean \| (storageValue, defaults) Tfalse是否用默认值合并存储值对象执行浅合并可传函数自定义合并逻辑serializerSerializerT按类型自动选择自定义序列化函数{ read, write }onError(error: unknown) voidconsole.error读写出错时的回调shallowbooleanfalse是否用shallowRef代替ref适合大数据量、只做整体替换的场景initOnMountedbooleanfalse是否等到组件 mounted 后再读取存储SSR 安全场景常用flushpre \| post \| syncprewatch 的触发时机与 Vue watch 语义一致eventFilterEventFilter无事件过滤器如debounceFilter(100)做防抖写入windowWindowdefaultWindow自定义 window 实例iframe、测试环境官方示例useLocalStorage(key, defaults, { // 深度监听对象/数组变化默认 true deep: true, // 通过 storage 事件跨标签页同步默认 true listenToStorageChanges: true, // 存储中不存在时写入默认值默认 true writeDefaults: true, // 使用 shallowRef 代替 ref默认 false shallow: false, // 组件 mounted 后才初始化读取默认 false initOnMounted: false, // 自定义错误处理默认 console.error onError: e console.error(e), // watch 触发时机默认 pre flush: pre, })其中eventFilter的用法在测试中有明确验证packages/core/useStorage/index.browser.test.ts 的eventFilter用例配合debounceFilter(100)后修改值不会立即写入存储而是等待 100ms 防抖窗口结束后才setItem适合高频更新场景import { debounceFilter } from vueuse/shared const ref useLocalStorage(key, { name: a, data: 123 }, { eventFilter: debounceFilter(100), }) ref.value.name b // 不会立即写入 // 100ms 后自动写入 {name:b,data:123}五、序列化机制与 StorageSerializerslocalStorage只能存储字符串。useStorage会根据initialValue的数据类型自动挑选序列化器。类型推断逻辑位于 packages/core/useStorage/guess.tsexport function guessSerializerTypeT(rawInit: T) { return rawInit null ? any : rawInit instanceof Set ? set : rawInit instanceof Map ? map : rawInit instanceof Date ? date : typeof rawInit boolean ? boolean : typeof rawInit string ? string : typeof rawInit object ? object : !Number.isNaN(rawInit) ? number : any }官方文档提供的内置序列化器一览源码实现见StorageSerializers定义类型说明写入实现读取实现string普通字符串String(v)原样返回number数字String(v)Number.parseFloat(v)boolean布尔值String(v)v trueobjectJSON 对象/数组JSON.stringify(v)JSON.parse(v)mapJavaScriptMapJSON.stringify(Array.from(entries))new Map(JSON.parse(v))setJavaScriptSetJSON.stringify(Array.from(v))new Set(JSON.parse(v))dateDate对象v.toISOString()new Date(v)any原始字符串透传String(v)原样返回5.1 自定义序列化当内置序列化器不满足需求例如存储加密数据、自定义格式时传入serializer: { read, write }import { useLocalStorage } from vueuse/core useLocalStorage(key, {}, { serializer: { read: (v: any) v ? JSON.parse(v) : null, write: (v: any) JSON.stringify(v), }, })5.2 默认值为 null 时必须显式指定序列化器官方文档特别提示当initialValue为null时guessSerializerType只能推断出any原始字符串透传无法自动处理对象。此时应复用内置序列化器import { StorageSerializers, useLocalStorage } from vueuse/core const objectLike useLocalStorage(key, null, { serializer: StorageSerializers.object }) objectLike.value { foo: bar } // 以 JSON 格式写入测试用例packages/core/useStorage/index.browser.test.ts验证了各种类型的序列化行为对象写入{name:a,data:123}、Map 写入[[1,a],[2,2]]、Set 写入[1,2]、Date 写入 ISO 字符串并支持深度修改后自动重新序列化。六、合并默认值 mergeDefaults默认行为是存储中已有值则直接采用存储值忽略默认值。这可能导致结构缺失——例如客户端存储的是旧版本的{hello: hello}而默认值新增了greeting字段localStorage.setItem(my-store, {hello: hello}) const state useLocalStorage(my-store, { hello: hi, greeting: hello }) console.log(state.value.greeting) // undefined存储中没有该字段启用mergeDefaults: true后对象会执行浅合并存储值覆盖同名属性默认值补充缺失属性localStorage.setItem(my-store, {hello: nihao}) const state useLocalStorage( my-store, { hello: hi, greeting: hello }, { mergeDefaults: true }, ) console.log(state.value.hello) // nihao来自存储 console.log(state.value.greeting) // hello来自合并的默认值对于数组浅合并直接采用存储数组测试中useStorage(k, [2], storage, { mergeDefaults: true })在存储为[1]时得到[1]。需要深层合并时可传自定义函数const state useLocalStorage( my-store, { hello: hi, greeting: hello }, { mergeDefaults: (storageValue, defaults) deepMerge(defaults, storageValue) }, )测试用例还覆盖了自定义函数的通用性(value, initial) value initial可实现数字累加(value, initial) [...initial, ...value]可实现数组合并见 packages/core/useStorage/index.browser.test.ts 的mergeDefaults option用例。注意mergeDefaults仅在**首次读取非事件驱动**时生效源码中该逻辑位于read()函数的!event mergeDefaults分支。七、跨标签页与同文档同步原理useLocalStorage默认开启listenToStorageChanges: true这是它实现多标签页自动同步的关键。源码的监听逻辑packages/core/useStorage/index.tsif (window listenToStorageChanges) { if (storage instanceof Storage) useEventListener(window, storage, onStorageEvent, { passive: true }) else useEventListener(window, customStorageEventName, onStorageCustomEvent) }标准StoragelocalStorage监听原生storage事件实现跨标签页同步。update()中会校验event.storageArea、event.key只处理属于当前 key 的变更自定义 StorageLike监听 VueUse 自定义事件vueuse-storage常量customStorageEventName实现同一文档内多个实例同步。因为StorageEvent无法用非内置 storageArea 构造写入时会dispatchEvent一个携带{ key, oldValue, newValue, storageArea }的自定义事件源码dispatchWriteEvent()。测试用例验证了两类同步// 同文档同步标准 Storage const state1 useStorage(KEY, 0, storage) const state2 useStorage(KEY, 0, storage) state1.value 1 await nextTick() expect(state2.value).toBe(1) // 通过自定义事件同步 // 跨标签页原生 storage 事件 window.dispatchEvent(new StorageEvent(storage, { storageArea: localStorage, key: KEY, newValue: 1 })) expect(data.value).toBe(1)整个读写流程是写时序列化、读时反序列化、变更双向驱动的闭环ref变化 →watchPausable触发write(newValue)→ 序列化 →setItem/removeItemnull时删除storage事件/自定义事件到达 →update()→read()反序列化 → 更新ref期间pauseWatch暂停写回避免无限循环事件驱动时用nextTick(resumeWatch)恢复见源码update()的finally块。八、响应式 Keykey 可动态变化key支持 ref 或 getterkey 变化时自动读取新位置的存储数据import { ref } from vue import { useLocalStorage } from vueuse/core const userId ref(user-1) const userData useLocalStorage( () user-data-${userId.value}, { name: }, ) // 切换用户自动从 user-data-2 读取数据 userId.value user-2实现原理源码中用computed(() toValue(key))生成keyComputed并watch(keyComputed, () update())。测试updates on key change when the new storage value is presented验证了key 切换到已有数据的另一个 key 时ref会更新为新存储值而changes to defaults on key change when the new storage value is undefined验证了新 key 无数据时回落到默认值。测试同时确认初始 key 位置的数据会保留writeDefaults: true会把当时的默认值写回原 key切换不会丢失旧数据。九、SSR / Nuxt 注意事项官方文档对 Nuxt 3 有一条重要提示在 Nuxt 3 中使用时该函数不会被自动导入因为会与 Nitro 内置的useStorage()冲突。如果希望使用 VueUse 的这个函数请使用显式导入。import { useLocalStorage } from vueuse/core此外服务端渲染环境下window不存在。useLocalStorage的处理方式是defaultWindow isClient ? window : undefined见 packages/core/_configurable.tsSSR 时window为undefinedwindow?.localStorage为undefineduseStorage检测到!storage时会通过getSSRHandler(getDefaultStorage, () defaultWindow?.localStorage)获取存储源见 packages/core/ssr-handlers.ts允许在服务端注入自定义处理器若最终仍无存储可用则退化为纯内存 ref源码if (!storage) return data不会抛出异常但也不具备持久化能力。需要真正SSR 安全 客户端持久化时配合initOnMounted: true让初始化读取推迟到组件挂载之后测试initOnMounted用例验证了挂载前返回默认值、挂载后读取真实存储值的时序。十、常见边界行为速查state.value null/undefined触发removeItem删除该 key测试remove value用例验证删除后getItem返回 falsy存储值优先key 已存在时采用存储值测试use storage value if present用例逐一验证了字符串、数字、布尔、对象、空字符串、Map、Set 的存储优先行为writeDefaultskey 不存在且默认值非空时首次初始化即把默认值写入存储shallow: true内部使用shallowRef深度修改对象不会触发写入只有整体替换.value才同步错误处理任何读写异常都会进入onError默认console.error不会中断应用测试handle error用例模拟了setItem抛错与 SSR 处理器抛错两种场景。十一、源码结构速览围绕本主题可继续深入阅读的仓库文件packages/core/useLocalStorage/index.tsuseLocalStorage本体固定 localStorage 的薄封装packages/core/useLocalStorage/index.md官方文档本文骨架来源packages/core/useStorage/index.tsuseStorage完整实现、UseStorageOptionsT、StorageSerializerspackages/core/useStorage/guess.ts序列化器类型自动推断packages/core/useStorage/index.mduseStorage 完整官方文档packages/core/useStorage/index.browser.test.ts覆盖序列化、合并、同步、事件过滤、SSR 处理器等 30 余个测试用例packages/core/useStorage/demo.vue官方演示组件packages/core/ssr-handlers.tsSSR 存储处理器getSSRHandler/setSSRHandler总结useLocalStorage的价值在于把繁琐的存储读写收敛为一行代码类型安全的响应式 ref、自动序列化、跨标签页同步、动态 key、默认值合并与 SSR 兼容全部由useStorage的底层实现统一提供。掌握本文覆盖的类型签名、UseStorageOptions各参数、StorageSerializers选择逻辑与同步原理之后你可以在任何 Vue 3 项目中安全、高效地使用它来持久化用户偏好、草稿、配置等状态数据。若需要会话级存储可参考同族的useSessionStorage需要异步存储后端如 IndexedDB则参考useStorageAsync。赞分享前端【免费下载链接】vueuseCollection of essential Vue Composition Utilities for Vue 3项目地址https://gitcode.com/gh_mirrors/vu/vueuse点击查看免费下载相关推荐VueUse useStorage 完全指南响应式 LocalStorage/SessionStorage 绑定实战与源码解析VueUse useStorage 完全指南响应式 LocalStorage/SessionStorage 绑定实战与源码解析 导读 useStorage 是前端VueUse useGeolocation 完全指南基于原生 Geolocation API 的响应式定位封装VueUse useGeolocation 完全指南基于原生 Geolocation API 的响应式定位封装 useGeolocation 是 VueUse前端VueUse useLocalStorage 实战指南让 localStorage 响应式地与 Vue 3 状态同步VueUse useLocalStorage 实战指南让 localStorage 响应式地与 Vue 3 状态同步 导读 useLocalStorage 是前端上一篇终极指南3分钟学会一键下载国家中小学智慧教育平台电子课本下一篇3分钟完全掌握Super IO剪贴板插件让Blender导入导出效率翻倍创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表