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。
工作原理
- 在 VueUse
useLocalStorage/useSessionStorage之上注入自定义 serializer:读写时透明调用 aes 的decryptData/encryptData,密钥取runtimeConfig.public.cacheAesKey。 - 键名自动拼接
runtimeConfig.public.cachePrefix,多项目共用域名时互不干扰。 - SSR 环境下降级为
initialValue,不访问window—— 服务端也能安全调用。 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) => RemovableRef | localStorage 响应式 Ref,直接赋值写入,.remove() 删除该 key |
clearLocal | () => void | 清空整个 localStorage(含非本项目 key,慎用);服务端 no-op |
useSession | <T>(key: string, initialValue: T) => RemovableRef | sessionStorage 响应式 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)的 logout 全清可接受;共用域名 / 只清本模块时改用各 Ref 的.remove()精确删。- hydration mismatch:服务端首次渲染返回
initialValue,水合后切换到客户端实际值;若初值与实际不同且直接渲染进模板,可能触发 hydration 告警,可搭配<ClientOnly>规避。 - 敏感字段(密码 / 私钥)禁止放 cookie(随每个请求上线路 + DevTools 可见),应放
useLocal(仅主动 JS 读取时使用)。
useCookie(加密 cookie)
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)。