層)
深入理解 VueUseuseStorage為 Vue 3 應用打造響應式 Web Storage 狀態(tài)層【免費下載鏈接】airi Self hosted, you-owned Grok Companion, a container of souls of waifu, cyber livings to bring them into our worlds, wishing to achieve Neuro-samas altitude. Capable of realtime voice chat, Minecraft, Factorio playing. Web / macOS / Windows supported.項目地址: https://gitcode.com/GitHub_Trending/ai/airi導讀useStorage是 VueUse 狀態(tài)State類別中用于把 Vue 響應式ref與瀏覽器localStorage/sessionStorage雙向綁定的核心組合式函數(shù)。在 airi 這類橫跨 Web、PWACapacitor與 Electron 桌面的多端應用中它被大量用于持久化設置項并進一步被封裝為帶版本校驗、手動重置能力的本地存儲抽象。讀完本文你將掌握useStorage的完整調(diào)用姿勢、默認值與序列化機制、全部配置項的含義以及如何借鑒 airi 倉庫中的版本化封裝為生產(chǎn)級應用設計可靠的本地持久化方案。useStorage創(chuàng)建的是一個可以直接讀寫、自動同步存儲介質(zhì)的響應式引用reactive ref默認綁定localStorage也可通過第三個參數(shù)指定sessionStorage或其他StorageLike對象。本文以倉庫內(nèi) useStorage.md 為骨架結合 airi 源碼中的真實封裝與測試展開講解?;居梅ㄒ恍写a把響應式狀態(tài)落到瀏覽器存儲useStorage最直觀的價值在于你無需再手動getItem/setItem 監(jiān)聽事件來同步狀態(tài)它根據(jù)傳入默認值的類型自動選擇序列化方式并返回一個帶類型的RemovableRef。import { useStorage } from vueuse/core // 綁定對象JSON 序列化 const state useStorage(my-store, { hello: hi, greeting: Hello }) // 綁定布爾值返回 Refboolean const flag useStorage(my-flag, true) // 綁定數(shù)字返回 Refnumber const count useStorage(my-count, 0) // 綁定字符串并指定 sessionStorage返回 Refstring const id useStorage(my-id, some-string-id, sessionStorage) // 刪除存儲中的數(shù)據(jù) state.value null幾點值得注意刪除數(shù)據(jù)把state.value賦值為null會調(diào)用存儲介質(zhì)的removeItem這正是RemovableRef語義的體現(xiàn)。存儲介質(zhì)可替換第三個參數(shù)只要是滿足getItem/setItem/removeItem接口的對象即可因此useStorage天然支持單元測試中注入內(nèi)存存儲替身——airi 的測試里就是這么做的見下文。返回值帶類型不同默認值類型會命中不同的函數(shù)重載返回Refboolean、Refnumber、Refstring或RefT。Nuxt 3 使用提示當在 Nuxt 3 中使用時該函數(shù)不會被自動導入以免與 Nitro 內(nèi)置的同名useStorage()沖突。若你確實要使用 VueUse 版本請顯式import { useStorage } from vueuse/core。airi 倉庫的所有應用如 apps/stage-pocket/package.json、apps/stage-tamagotchi/package.json、apps/component-calling/package.json也都是通過顯式聲明vueuse/core依賴catalog 版本管理來使用這套 API 的。默認值合并策略Merge Defaults避免新增字段變成undefined默認情況下只要存儲里已存在該 key 的值useStorage就會直接使用存儲值而忽略默認值。這意味著當你給默認對象新增屬性時老用戶存儲中沒有這個 key 的對應字段讀取結果會是undefinedimport { useStorage } from vueuse/core localStorage.setItem(my-store, {hello: hello}) const state useStorage(my-store, { hello: hi, greeting: hello }, localStorage) console.log(state.value.greeting) // undefined因為存儲中沒有該字段要解決這種存儲值落后于代碼默認值的問題可以開啟mergeDefaults選項import { useStorage } from vueuse/core localStorage.setItem(my-store, {hello: nihao}) const state useStorage( my-store, { hello: hi, greeting: hello }, localStorage, { mergeDefaults: true }, // -- 開啟合并 ) console.log(state.value.hello) // nihao來自存儲 console.log(state.value.greeting) // hello來自合并進來的默認值合并規(guī)則說明mergeDefaults: true時對對象執(zhí)行淺合并shallow merge存儲中已有的字段以存儲值為準默認值中新增的字段被補入。也可以傳入自定義合并函數(shù)例如實現(xiàn)深合并import { useStorage } from vueuse/core const state useStorage( my-store, { hello: hi, greeting: hello }, localStorage, { mergeDefaults: (storageValue, defaults) deepMerge(defaults, storageValue) }, )倉庫實踐版本化合并的思路升級airi 在 packages/stage-shared/src/composables/use-versioned-local-storage/index.ts 中把合并默認值升級為版本化存儲寫入 localStorage 的值統(tǒng)一包裝為{ version, data }結構每次讀取時用satisfiesVersionBy回調(diào)比較存儲版本與當前defaultVersion不滿足版本要求時走onVersionMismatch策略keep保留舊值或reset重置為默認值從而以聲明方式解決代碼升級后舊數(shù)據(jù)不兼容的問題。其測試 use-versioned-local-storage/index.test.ts 用一個MemoryStorage僅實現(xiàn)getItem/setItem/removeItem的最小StorageLike驗證了寫入settings/live2d/auto-blink-enabled時會持久化為{ version: 2.0.0, data: false }的包裝結構——這也是useStorage支持自定義存儲介質(zhì)這一設計帶來的直接收益。自定義序列化Custom Serialization從 JSON 到 Map / Set / DateuseStorage會根據(jù)默認值類型智能挑選序列化器對象走JSON.stringify/JSON.parse數(shù)字走Number.toString/parseFloat等等。你也可以完全接管序列化過程import { useStorage } from vueuse/core useStorage( key, {}, undefined, { serializer: { read: (v: any) v ? JSON.parse(v) : null, write: (v: any) JSON.stringify(v), }, }, )需要注意當默認值為null時useStorage無法從類型推斷序列化方式此時應顯式提供自定義序列化器或復用內(nèi)置序列化器import { StorageSerializers, useStorage } from vueuse/core const objectLike useStorage(key, null, undefined, { serializer: StorageSerializers.object }) objectLike.value { foo: bar }內(nèi)置序列化器一覽StorageSerializersStorageSerializers提供了以下開箱即用的序列化器類型說明string普通字符串原樣讀寫number數(shù)字經(jīng)parseFloat讀取boolean布爾值objectJSON 對象/數(shù)組mapJavaScriptMapsetJavaScriptSetdateJavaScriptDate經(jīng)toISOString寫入any原始字符串直通例如把Map持久化到存儲import { StorageSerializers, useStorage } from vueuse/core const myMap useStorage(my-map, new Map(), undefined, { serializer: StorageSerializers.map, })Options 完整配置項useStorage的第四個參數(shù)接受UseStorageOptionsT完整的調(diào)用形態(tài)與注釋如下useStorage(key, defaults, storage, { // 深度監(jiān)聽對象/數(shù)組內(nèi)部變化默認 true deep: true, // 通過 storage 事件跨標簽頁同步默認 true listenToStorageChanges: true, // 存儲中不存在時把默認值寫入存儲默認 true writeDefaults: true, // 使用 shallowRef 而非 ref默認 false shallow: false, // 僅在組件掛載后再初始化讀取默認 false initOnMounted: false, // 自定義錯誤處理默認 console.error onError: e console.error(e), // watch 刷新時機默認 pre flush: pre, })各選項的底層影響deep決定內(nèi)部watch是否深度跟蹤對象/數(shù)組的嵌套變化進而決定嵌套字段修改時是否觸發(fā)回寫。listenToStorageChanges監(jiān)聽storage事件實現(xiàn)多標簽頁同步。開啟時跨標簽頁的修改會實時反映到當前頁面airi 的封裝中該選項同樣被透傳見 use-local-storage-manual-reset/index.ts其中options?.listenToStorageChanges ! false時才同步存儲來源的變更。writeDefaults首次訪問時若存儲中無此 key把默認值寫入存儲避免下次讀取時拿到null。shallow對大型/復雜對象可減少深層響應式開銷。initOnMounted延遲到onMounted后再讀取存儲適合 SSR 場景避免在服務端訪問window.localStorage。onError統(tǒng)一接管解析失敗、寫入異常等錯誤便于接入上報。flush沿用 Vuewatch的 flush 語義pre/post/sync決定回寫時機。Reactive Key讓存儲鍵本身可響應存儲鍵可以是ref或 getter 函數(shù)當 key 變化時useStorage會從新的存儲位置讀取數(shù)據(jù)import { useStorage } from vueuse/core const userId ref(user-1) const userData useStorage( () user-data-${userId.value}, { name: }, ) // 切換 key 后將從新的存儲位置讀取 userId.value user-2這一特性非常適合多實例狀態(tài)各自持久化的場景例如按會話、按角色、按賬號維度隔離本地數(shù)據(jù)。倉庫中的組合式實踐useLocalStorageManualResetairi 在 packages/stage-shared/src/composables/use-local-storage-manual-reset/index.ts 中把useLocalStorage即固定為localStorage的useStorage便捷封裝與refManualReset組合構造出可手動重置的本地存儲 ref用useLocalStorageT(key, value, options)建立持久化層外層用refManualReset包裝暴露reset()語義通過雙向 watch 在用戶寫入與存儲來源變更之間橋接并利用toRaw比較避免同值回寫引發(fā)的二次 Pinia 變更循環(huán)見源碼注釋。該封裝被實際用于 packages/stage-ui/src/stores/settings/general.ts 的 Pinia store 中持久化settings/language、settings/disable-transitions、settings/websocket/secure-enabled等設置項并提供了resetState()一鍵恢復默認值的能力const language useLocalStorageManualResetstring(settings/language, ) const disableTransitions useLocalStorageManualResetboolean(settings/disable-transitions, true) function resetState() { language.reset() disableTransitions.reset() // ... }這類ref storage 手動重置的組合正是useStorage在真實項目中作為基礎構件被二次封裝、融入 Pinia 狀態(tài)管理的典型范式。類型聲明與擴展接口useStorage相關的完整類型聲明來自 useStorage.md如下它揭示了自定義序列化器與事件過濾等擴展點export interface SerializerT { read: (raw: string) T write: (value: T) string } export interface SerializerAsyncT { read: (raw: string) AwaitableT write: (value: T) Awaitablestring } export declare const StorageSerializers: Record boolean | object | number | any | string | map | set | date, Serializerany export declare const customStorageEventName vueuse-storage export interface StorageEventLike { storageArea: StorageLike | null key: StorageEvent[key] oldValue: StorageEvent[oldValue] newValue: StorageEvent[newValue] } export interface UseStorageOptionsT extends ConfigurableEventFilter, ConfigurableWindow, ConfigurableFlush { deep?: boolean // 深度監(jiān)聽默認 true listenToStorageChanges?: boolean // 監(jiān)聽 storage 變化默認 true writeDefaults?: boolean // 寫入默認值默認 true mergeDefaults?: boolean | ((storageValue: T, defaults: T) T) // 合并默認值默認 false serializer?: SerializerT // 自定義序列化 onError?: (error: unknown) void // 錯誤回調(diào)默認 console.error shallow?: boolean // 使用 shallowRef默認 false initOnMounted?: boolean // 掛載后初始化默認 false }重載簽名覆蓋了string/boolean/number/ 泛型T/null五種形態(tài)其中defaults: null時返回RemovableRefT配合顯式serializer使用。此外它還繼承了ConfigurableEventFilter、ConfigurableWindow、ConfigurableFlush三個可配置接口分別用于事件過濾如eventFilter、窗口對象注入如 SSR 中的window與 watch 刷新時機控制這為在非瀏覽器環(huán)境如 Node 測試、Electron 主進程復用該 API 提供了統(tǒng)一入口。結語useStorage用極小的 API 面覆蓋了 Web Storage 持久化的全部關鍵訴求類型化響應式綁定、智能序列化、默認值合并、多標簽頁同步、可響應 key 與可插拔存儲介質(zhì)。airi 倉庫中的 use-versioned-local-storage、use-local-storage-manual-reset 及其配套測試展示了如何在其之上構建版本兼容與手動重置等生產(chǎn)級能力可作為你在自己的 Vue 3 項目中設計本地持久化層的直接參考。如果你還需要異步存儲如 IndexedDB、自定義異步后端可以繼續(xù)閱讀同一 skill 目錄下的 useStorageAsync.md 與 useLocalStorage.md、useSessionStorage.md 等姊妹文檔?!久赓M下載鏈接】airi Self hosted, you-owned Grok Companion, a container of souls of waifu, cyber livings to bring them into our worlds, wishing to achieve Neuro-samas altitude. Capable of realtime voice chat, Minecraft, Factorio playing. Web / macOS / Windows supported.項目地址: https://gitcode.com/GitHub_Trending/ai/airi創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考