useLocal / useSession

帶 AES 加密的瀏覽器儲存響應式封裝:useLocal(localStorage,跨工作階段持久)與 useSession(sessionStorage,關分頁即清),以及對應的全量清空函式 clearLocal / clearSession。4 個匯出全部 auto-import。

📌 前端快取一律走這兩個 util,禁止裸用 localStorage / sessionStorage / document.cookie(CACHE-001,blocker)。裸用漏三件事:漏 cachePrefix(多專案共用網域時 key 互撞串資料)、明文儲存(無 AES)、模組頂層存取在 SSR 期 500。例外僅限第三方 SDK 自管的 storage。

運作原理

  1. 在 VueUse useLocalStorage / useSessionStorage 之上注入自訂 serializer:讀寫時透明呼叫 aes 的 decryptData / encryptData,金鑰取 runtimeConfig.public.cacheAesKey。
  2. 鍵名自動拼接 runtimeConfig.public.cachePrefix,多專案共用網域時互不干擾。
  3. SSR 環境下降級為 initialValue,不存取 window —— 伺服器端也能安全呼叫。
  4. clearLocal / clearSession 在伺服器端為 no-op。

設定在 nuxt.config.ts 的 runtimeConfig.public(專案常數直接寫實值,隨程式碼走;執行期同名 NUXT_PUBLIC_* 程序環境變數仍可臨時覆蓋):

cachePrefix: 'c_'                 // 快取 key 前綴
cacheAesKey: 'your-16-byte-key'   // AES 金鑰(16 / 24 / 32 位元組)

⚠️ cacheAesKey 在 runtimeConfig.public.*,會進用戶端 bundle —— 加密僅作 obfuscation,不是安全屏障。詳見用戶端加密的安全邊界。

⚠️ cacheAesKey 必須是 16 / 24 / 32 位元組(按 utf8 位元組數計)。非法長度時 CryptoJS 加密能產出密文、解密回空 —— 寫入成功、讀取永遠失敗、且無任何報錯(靜默資料遺失)。framework 在建置期與 dev 執行期都會檢出並告警。

API

函式簽名說明
useLocal<T>(key: string, initialValue: T) => RemovableReflocalStorage 響應式 Ref,直接賦值寫入,.remove() 刪除該 key
clearLocal() => void清空整個 localStorage(含非本專案 key,慎用);伺服器端 no-op
useSession<T>(key: string, initialValue: T) => RemovableRefsessionStorage 響應式 Ref,分頁關閉自動清除
clearSession() => void清空整個 sessionStorage;伺服器端 no-op

使用範例

useLocal 寫讀 + localStorage 密文對照

加载中...
// 使用者偏好(長期保留 → useLocal)
const prefs = useLocal('ui-prefs', { theme: 'auto', lang: 'zh-CN' })
prefs.value.theme = 'dark' // 加密寫入

// 多步表單草稿(關分頁即清 → useSession)
const draft = useSession('post-draft', '')
draft.value = '暫存的正文…'
draft.remove() // 刪除該 key

// typed nullable:賦 null 即刪除
const account = useLocal<AccountInfo | null>('account', null)
account.value = null

// 登出時全量清理
function logout() {
  clearLocal()
  clearSession()
}

生命週期選型(CACHE-002,warning)

這份資料…用
要跨工作階段保留 / 跨分頁共享(偏好、草稿匣、記住我)useLocal
一次性 / 單分頁中轉(多步表單草稿、列表→詳情標誌)useSession

典型翻車:長期偏好用了 useSession(關分頁資料沒了);想跨分頁同步的狀態用了 useSession(另一個分頁讀不到)。

注意事項

  • clearLocal() / clearSession() 是全清、不按 cachePrefix 過濾(CACHE-003):內部就是裸 localStorage.clear() —— 多專案共用網域時會把其它專案的資料一起清掉。獨佔網域(絕大多數 applier)的登出全清可接受;共用網域 / 只清本模組時改用各 Ref 的 .remove() 精確刪。
  • hydration mismatch:伺服器端首次渲染回傳 initialValue,水合後切換到用戶端實際值;若初值與實際不同且直接渲染進模板,可能觸發 hydration 警告,可搭配 <ClientOnly> 規避。
  • 敏感欄位(密碼 / 私鑰)禁止放 cookie(隨每個請求上線路 + DevTools 可見),應放 useLocal(僅主動 JS 讀取時使用)。

framework 以同名 auto-import 覆蓋了 Nuxt 內建 useCookie(framework#199)——applier 裸寫 useCookie(key, opts) 拿到的即 framework 加密版:鍵名自動拼 cachePrefix、值經 AES 透明加解密(與 useLocal / useSession 同一安全模型:obfuscation 非安全屏障),回傳 WritableComputedRef<T | null>,賦 null 即刪除。支援透傳 maxAge / sameSite / secure / path / domain / default。

const token = useCookie<string>('token', { maxAge: 60 * 60 * 24 * 30, sameSite: 'lax', secure: true })
token.value = 'abc'   // 自動加密寫入
token.value = null    // 刪除 cookie

需要 Nuxt 原生明文版時顯式 import { useCookie } from 'nuxt/app'(極少數場景,如讀第三方寫入的明文 cookie)。