Pinia 状态持久化:刷新页面不丢数据的完整方案

更新日期: 2026-04-28 阅读: 32 标签: Pinia

用 Vue3 做项目,Pinia 是官方推荐的状态管理工具。它很轻量,用起来也简单。但有一个问题:页面一刷新,Store 里的数据就全没了。用户登录状态、系统设置、表单填了一半的内容,这些跨会话的数据要保留下来,就得自己想办法。

pinia-plugin-persistedstate 这个插件就是解决这个问题的。它能自动把 Pinia 的状态存到本地,刷新后再读回来。下面从头讲怎么用。


一、插件怎么工作的

原理不复杂,就三步:

  • 当 Store 里的数据变了,插件自动把指定的字段存到 localStorage 或 sessionStorage

  • 页面刷新或重新打开时,插件在 Store 初始化阶段从本地存储读取数据,覆盖掉初始值

  • 底层用的是 Pinia 的 subscribe 方法监听数据变化,配合浏览器的存储 API 实现同步


二、安装和注册

安装插件

npm install pinia-plugin-persistedstate --save

全局注册

注册顺序很重要:先创建 Pinia 实例,再注册插件,最后挂载到 Vue 应用。

// src/main.js
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
import App from './App.vue'

const pinia = createPinia()
pinia.use(piniaPluginPersistedstate)

const app = createApp(App)
app.use(pinia)
app.mount('#app')


三、基础用法

最简单的配置

在 Store 里加一行 persist: true,插件会把整个 state 都存下来。

// src/stores/user.js
import { defineStore } from 'pinia'

export const useUserStore = defineStore('user', {
  state: () => ({
    token: '',
    username: '',
    isLogin: false,
    permissions: []
  }),
  actions: {
    login(userInfo) {
      this.token = userInfo.token
      this.username = userInfo.username
      this.isLogin = true
      this.permissions = userInfo.permissions
    },
    logout() {
      this.$reset()
    }
  },
  persist: true   // 开启持久化
})

默认规则:存储 key 用的是 Store 的 ID(比如 'user'),存储方式是 localStorage,存整个 state。

自定义配置(推荐)

极简配置会把所有字段都存了,有些字段其实没必要存。用对象形式可以精细控制。

// src/stores/user.js
export const useUserStore = defineStore('user', {
  state: () => ({
    token: '',
    username: '',
    avatar: '',
    isLogin: false,
    loginTime: null,
    permissions: []
  }),
  actions: {
    login(userInfo) {
      this.token = userInfo.token
      this.username = userInfo.username
      this.isLogin = true
      this.loginTime = new Date()
    },
    logout() {
      this.$reset()
    }
  },
  persist: {
    key: 'my-project-user-store',        // 自定义存储用的键名
    storage: sessionStorage,              // 用 sessionStorage(关标签页就丢),默认 localStorage
    paths: ['token', 'username', 'isLogin', 'permissions'],  // 只存这几个字段
    serializer: {                         // 自定义序列化,处理 Date 类型
      serialize: (value) => {
        const serialized = { ...value }
        if (serialized.loginTime) {
          serialized.loginTime = serialized.loginTime.getTime()
        }
        return JSON.stringify(serialized)
      },
      deserialize: (value) => {
        const parsed = JSON.parse(value)
        if (parsed.loginTime) {
          parsed.loginTime = new Date(parsed.loginTime)
        }
        return parsed
      }
    }
  }
})


四、模块化配置(多个 Store)

实际项目里会有多个 Store:用户、购物车、系统设置。每个可以单独配置。

目录结构

src/
├── stores/
│   ├── user.js
│   ├── cart.js
│   ├── settings.js
│   └── index.js
└── main.js

购物车模块

// src/stores/cart.js
import { defineStore } from 'pinia'

export const useCartStore = defineStore('cart', {
  state: () => ({
    cartList: [],
    totalPrice: 0,
    selectedIds: []
  }),
  actions: {
    addGoods(goods) {
      this.cartList.push(goods)
      this.totalPrice = this.cartList.reduce((sum, item) => sum + item.price * item.quantity, 0)
    }
  },
  persist: {
    key: 'my-project-cart-store',
    paths: ['cartList']   // totalPrice 可以算出来,不用存
  }
})

系统设置模块

// src/stores/settings.js
import { defineStore } from 'pinia'

export const useSettingsStore = defineStore('settings', {
  state: () => ({
    theme: 'light',
    fontSize: 14,
    language: 'zh-CN'
  }),
  actions: {
    changeTheme(theme) {
      this.theme = theme
    }
  },
  persist: {
    key: 'my-project-settings-store'
  }
})

组件里使用

<script setup>
import { storeToRefs } from 'pinia'
import { useUserStore } from '@/stores/user'
import { useCartStore } from '@/stores/cart'
import { useSettingsStore } from '@/stores/settings'

const userStore = useUserStore()
const cartStore = useCartStore()
const settingsStore = useSettingsStore()

const { username, isLogin } = storeToRefs(userStore)
const { cartList } = storeToRefs(cartStore)
const { theme } = storeToRefs(settingsStore)

const addToCart = () => {
  cartStore.addGoods({ id: 1, name: 'Vue教程', price: 99, quantity: 1 })
}

const toggleTheme = () => {
  settingsStore.changeTheme(theme.value === 'light' ? 'dark' : 'light')
}
</script>

<template>
  <div>
    <div v-if="isLogin">欢迎 {{ username }}</div>
    <div>购物车商品数量:{{ cartList.length }}</div>
    <button @click="addToCart">添加商品</button>
    <button @click="toggleTheme">切换主题</button>
  </div>
</template>


五、高级用法

用 Cookie 存储

有些场景要用 Cookie,比如跨域或者服务端渲染。可以自己封装一个:

// src/utils/cookieStorage.js
import Cookies from 'js-cookie'

export const cookieStorage = {
  setItem(key, value) {
    Cookies.set(key, value, { expires: 7, path: '/' })
  },
  getItem(key) {
    return Cookies.get(key)
  },
  removeItem(key) {
    Cookies.remove(key, { path: '/' })
  }
}

然后在 Store 里用:

import { cookieStorage } from '@/utils/cookieStorage'

persist: {
  key: 'user-cookie',
  storage: cookieStorage,
  paths: ['token', 'isLogin']
}

全局默认配置

如果多个 Store 有相同的配置,可以在注册插件时统一设置。

// src/main.js
pinia.use(
  piniaPluginPersistedstate({
    key: (id) => `my-project-${id}`,   // 所有 Store 的 key 都加前缀
    storage: localStorage,
    serializer: {
      serialize: (value) => JSON.stringify(value),
      deserialize: (value) => JSON.parse(value)
    }
  })
)

局部配置会覆盖全局配置。


六、常见问题和避坑

1. 刷新后状态没恢复

检查这几项:

  • 插件注册顺序对不对(先 createPinia,再 use 插件,最后 app.use)

  • Store 里有没有配置 persist

  • paths 里有没有包含要恢复的字段

  • 打开浏览器开发者工具,看 Application 里有没有对应的 key

  • state 里有没有函数、Symbol 这些不能序列化的东西

2. 解构后数据不更新

错误写法:

const { token, isLogin } = useUserStore()  // 这样会丢失响应式

正确写法:

import { storeToRefs } from 'pinia'
const userStore = useUserStore()
const { token, isLogin } = storeToRefs(userStore)  // 保留响应式

3. Date 对象变成字符串

Date 类型存到 localStorage 会自动变成字符串,取出来后没法调用 getFullYear 这些方法。解决方法就是用自定义 serializer 转成时间戳,读的时候再转回 Date。

4. 多个 Store 用了同一个 key

用户模块和购物车模块如果都用同一个 key,一个会覆盖另一个。每个 Store 的 key 必须唯一,建议加模块名前缀。


七、总结

几个要点记住就行:

  • 安装插件后在 Pinia 实例上注册,顺序不能错

  • 每个 Store 单独配置 persist,用 paths 指定要存的字段

  • 解构 state 记得用 storeToRefs

  • Date 等特殊类型要自定义序列化

  • 每个 Store 的 key 要唯一,最好加项目前缀

  • 生产环境按需选择 localStorage 还是 sessionStorage

本文内容仅供个人学习、研究或参考使用,不构成任何形式的决策建议、专业指导或法律依据。未经授权,禁止任何单位或个人以商业售卖、虚假宣传、侵权传播等非学习研究目的使用本文内容。如需分享或转载,请保留原文来源信息,不得篡改、删减内容或侵犯相关权益。感谢您的理解与支持!

链接: https://fly63.com/article/detial/13728

相关推荐

测试一下Pinia,Vuex 要出局了?

自从我开始使用Vue 3和组合API以来,我也尝试使用 Pinea 作为状态管理库。如果是从是 vue2 和 vuex 过来的,就会觉得用起来差别还是很大的。

全新的 Vue3 状态管理工具:Pinia

Vue3 发布已经有一段时间了,它采用了新的响应式系统,而且构建了一套全新的 Composition API。Vue 的周边生态都在加紧适配这套新的系统,官方的状态管理库 Vuex 也在适配中

快速入门Pinia状态管理库

Pinia 是一个用于 Vue 的状态治理库,相似 Vuex, 是 Vue 的另一种状态治理计划。如果你现在使用 vue3 开发项目,那么推荐你使用 Pinia 开发。

Vue新一代状态管理插件Pinia

如果你之前使用过 vuex 进行状态管理的话,那么 pinia 就是一个类似的插件。它是最新一代的轻量级状态管理插件。按照尤雨溪的说法,vuex 将不再接收新的功能,建议将 Pinia 用于新的项目。

Pinia是Vuex的良好替代品吗?

Pinia 是 Vue.js 的轻量级状态管理库,最近很受欢迎。它使用 Vue 3 中的新反应系统来构建一个直观且完全类型化的状态管理库。

Pinia被鼓吹的setup写法真的好用吗

引用官方的一句话:Pinia是一个符合直觉的 Vue.js 状态管理库。简单说几点它的特性:它支持Vue3,同时也支持Vue2,是Vuex的完美过渡替代者

一文解析 Pinia 和 Vuex ,带你全面理解这两个 Vue 状态管理模式

Pinia和Vuex一样都是是vue的全局状态管理器。其实Pinia就是Vuex5,只不过为了尊重原作者的贡献就沿用了这个看起来很甜的名字Pinia。本文将通过Vue3的形式对两者的不同实现方式进行对比,让你在以后工作中无论使用到Pinia还是Vuex的时候都能够游刃有余。

我使用 Pinia 的 5 大技巧

在这篇文章中,想与大家分享使用 Pinia 的五大技巧。以下是简要总结:不要创建无用的 getter,在 Option Stores 中使用组合式函数(composables),对于复杂的组合式函数,使用 Setup Stores

Pinia 3.0 正式发布,不再支持 Vue 2

Vue 官方推荐的状态管理工具 Pinia 最近更新到了 3.0 版本。这个版本有一个最重要的变化:它彻底放弃了对 Vue 2 的支持,现在只专注于 Vue 3 的生态。

Vue状态管理:Pinia与Vuex全面对比

在Vue应用开发中,随着项目规模的增长,组件之间的数据共享变得越来越复杂。状态管理工具就是为了解决这个问题而出现的。

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!