文章目录
-
- 一、核心原理
- 二、核心配置项(完整)
- [三、Vue3 完整使用步骤](#三、Vue3 完整使用步骤)
-
- [1. 安装依赖](#1. 安装依赖)
- [2. 全局注册插件(main.ts/main.js)](#2. 全局注册插件(main.ts/main.js))
- [3. Store 中启用持久化(两种风格)](#3. Store 中启用持久化(两种风格))
-
- [(1)Option Store 风格(选项式)](#(1)Option Store 风格(选项式))
- [(2)Setup Store 风格(组合式,推荐 Vue3)](#(2)Setup Store 风格(组合式,推荐 Vue3))
- [4. 多策略持久化(进阶)](#4. 多策略持久化(进阶))
- [5. 组件中使用](#5. 组件中使用)
- 四、常见场景与最佳实践
- 五、常见问题

pinia-plugin-persistedstate 是 Pinia 官方生态中最主流的状态持久化插件 ,核心作用是将 Pinia 的状态自动同步到本地存储(如 localStorage/sessionStorage),页面刷新后状态不丢失,解决内存状态易重置的痛点。它配置极简、支持细粒度控制、兼容 Vue3 与 Pinia 全版本,是 Vue3 项目状态持久化的首选方案。
一、核心原理
- 插件通过 Pinia 的插件机制 全局注册,监听所有 Store 的状态变更。
- 状态更新时,自动序列化并写入指定存储介质(默认 localStorage)。
- 页面初始化时,自动反序列化读取存储数据,覆盖到对应 Store 的 state。
- 支持全局配置 + 单个 Store 配置,优先级:Store 配置 > 全局配置。
二、核心配置项(完整)
persist 支持布尔值(简单开启)或对象(精细配置),完整配置如下:
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
enabled |
boolean |
true |
是否启用持久化 |
key |
string |
Store ID | 存储在 localStorage 中的键名(建议自定义避免冲突) |
storage |
Storage |
localStorage |
存储介质:localStorage/sessionStorage/自定义存储 |
paths |
string[] |
undefined |
仅持久化指定字段(路径数组,如 ['user.token', 'theme']) |
serializer |
object |
JSON |
自定义序列化/反序列化:{ serialize, deserialize } |
beforeRestore |
() => void |
- | 状态恢复前钩子 |
afterRestore |
() => void |
- | 状态恢复后钩子 |
strategies |
array |
- | 多策略配置(不同字段用不同存储) |
三、Vue3 完整使用步骤
1. 安装依赖
bash
# npm
npm install pinia pinia-plugin-persistedstate
# yarn
yarn add pinia pinia-plugin-persistedstate
# pnpm
pnpm add pinia pinia-plugin-persistedstate
2. 全局注册插件(main.ts/main.js)
typescript
import { createApp } from 'vue'
import { createPinia } from 'pinia'
// 引入持久化插件
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
import App from './App.vue'
const app = createApp(App)
const pinia = createPinia()
// 注册插件(全局生效)
pinia.use(piniaPluginPersistedstate)
app.use(pinia)
app.mount('#app')
3. Store 中启用持久化(两种风格)
(1)Option Store 风格(选项式)
最简用法(全部字段持久化到 localStorage)
typescript
// src/stores/counter.ts
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', {
state: () => ({
count: 0,
isLoading: false
}),
actions: {
increment() {
this.count++
}
},
// ✅ 开启持久化(默认配置)
persist: true
})
精细配置(推荐生产环境)
typescript
// src/stores/user.ts
import { defineStore } from 'pinia'
export const useUserStore = defineStore('user', {
state: () => ({
token: '',
userInfo: {
id: '',
name: '',
avatar: ''
},
tempData: null // 不需要持久化
}),
actions: {
setToken(token: string) {
this.token = token
},
logout() {
this.token = ''
this.userInfo = { id: '', name: '', avatar: '' }
// 清除本地存储
localStorage.removeItem('app-user-store')
}
},
// ✅ 自定义持久化配置
persist: {
key: 'app-user-store', // 自定义存储键
storage: localStorage, // 存储介质
paths: ['token', 'userInfo'], // 仅持久化指定字段
// 自定义序列化(处理 Date 等特殊类型)
serializer: {
serialize: (value) => JSON.stringify(value),
deserialize: (value) => JSON.parse(value)
},
// 恢复钩子
afterRestore: (ctx) => {
console.log('用户状态已恢复', ctx.store.$state)
}
}
})
(2)Setup Store 风格(组合式,推荐 Vue3)
typescript
// src/stores/setting.ts
import { defineStore } from 'pinia'
import { ref, reactive } from 'vue'
export const useSettingStore = defineStore('setting', () => {
// 状态
const theme = ref('light')
const language = ref('zh-CN')
const collapsed = ref(false)
// 动作
const setTheme = (val: string) => {
theme.value = val
}
// ✅ 持久化配置(第三个参数)
return { theme, language, collapsed, setTheme }
}, {
persist: {
key: 'app-setting',
storage: sessionStorage, // 会话存储(关闭标签页清空)
paths: ['theme', 'language'] // 不持久化 collapsed
}
})
4. 多策略持久化(进阶)
不同字段使用不同存储介质:
typescript
persist: {
strategies: [
// token 长期保存到 localStorage
{ key: 'auth-token', storage: localStorage, paths: ['token'] },
// 主题仅会话保存到 sessionStorage
{ key: 'ui-theme', storage: sessionStorage, paths: ['theme'] }
]
}
5. 组件中使用
vue
<script setup lang="ts">
import { useCounterStore } from '@/stores/counter'
import { useUserStore } from '@/stores/user'
import { storeToRefs } from 'pinia' // 保持解构响应式
const counterStore = useCounterStore()
const userStore = useUserStore()
// 解构状态(必须用 storeToRefs)
const { count } = storeToRefs(counterStore)
const { token, userInfo } = storeToRefs(userStore)
// 调用方法
const add = () => counterStore.increment()
</script>
<template>
<div>
<h2>计数:{{ count }}</h2>
<button @click="add">+1</button>
<div v-if="token">
<p>用户名:{{ userInfo.name }}</p>
<button @click="userStore.logout">退出登录</button>
</div>
<div v-else>未登录</div>
</div>
</template>
四、常见场景与最佳实践
- 只持久化必要字段 :用
paths过滤,减少存储体积。 - 敏感信息处理:token 等可持久化,临时数据不要持久化。
- 存储介质选择 :
localStorage:长期保存(关闭浏览器仍在)sessionStorage:会话保存(标签页关闭即清空)
- 清除持久化 :登出时用
localStorage.removeItem(key)或store.$reset()。 - TypeScript 支持:完美兼容,无需额外配置。
五、常见问题
- 刷新后状态仍丢失 :检查
persist是否开启、paths路径是否正确、存储键是否冲突。 - 特殊类型(Date/RegExp)无法存储 :自定义
serializer序列化。 - SSR 兼容 :需判断环境,避免服务端访问
window对象。