开发 Vue 项目时常见一个现象:路由跳转正常,但按下 F5 刷新后,Vuex 中的 token、userInfo、searchForm 全部回到初始值。排查后确认不是业务代码 bug,而是 Vuex 的设计使然——state 只保存在内存中,刷新页面会销毁当前应用进程并重新创建 store,内存数据自然被重置。如果每次刷新都重新请求接口恢复状态,既增加请求也可能出现页面闪烁。
针对这个问题,一条较为稳妥的落地路径是:用 vuex-persistedstate 把需要保留的 state 缓存到 localStorage,初始化时自动读回。下面是配置与几个容易踩坑的细节。
一、安装与 Store 配置
安装命令:- npm install vuex-persistedstate --save
复制代码
在 store/index.js 中引入插件并在 plugins 中启用,key 用于指定 localStorage 的缓存键名:- import Vue from 'vue'
- import Vuex from 'vuex'
- import persistedState from 'vuex-persistedstate'
- Vue.use(Vuex)
- export default new Vuex.Store({
- state: {
- token: '',
- userInfo: {},
- searchForm: {}
- },
- mutations: {},
- actions: {},
- // 配置持久化
- plugins: [persistedState({ key: 'vuex_cache' })]
- })
复制代码
这段配置的含义是:插件会自动把 store.state 序列化写入 localStorage 的 vuex_cache 键,并在应用启动时读取该键恢复 state。配置完成后刷新页面,token、用户信息等不再丢失,也不需要重复调用接口,页面不会因等待接口而闪烁。该方案配置量小、对业务代码侵入低,适合 Vue2 常规项目。
二、避坑一:不要全量持久化
使用持久化插件时,不要把所有 state 都缓存下来。列表数据、临时弹窗状态、实时动态数据不适合缓存,这类数据变化频繁、时效性强,缓存后容易产生脏数据,甚至导致页面状态错乱。实践上只保留 token、用户信息、全局固定配置等真正需要跨刷新复用的字段即可。这里的核心原则是:不要全量持久化。
三、避坑二:退出登录必须手动清缓存
只清空 Vuex state 并不够,localStorage 中的 vuex_cache 仍然保留旧数据,重新进入页面时插件会把旧 token 和用户信息读回来,造成登录状态异常。退出登录时应同时重置 store 并删除缓存键:- logout() {
- this.$store.commit('resetState')
- localStorage.removeItem('vuex_cache')
- this.$router.replace('/login')
- }
复制代码
其中 resetState 是用于把 state 恢复到初始值的 mutation,vuex_cache 必须与 plugins 中配置的 key 保持一致。
四、其他方案与适用场景
如果项目使用 Pinia(Vue3 首选),可以换成 pinia-plugin-persistedstate:安装 npm i pinia-plugin-persistedstate,在 main.ts 中注册插件,然后在需要持久化的 Store 中开启 persist:- import { defineStore } from 'pinia'
- export const useUserStore = defineStore('user', {
- state: () => ({
- token: '',
- userInfo: null
- }),
- // 开启持久化
- persist: true // 默认存 localStorage,刷新后自动恢复
- })
复制代码
若没有使用状态管理库,或只想保存某个关键变量,可以直接操作 Web Storage。核心逻辑是页面加载时读取、数据变化时写入:- import { ref, watch, onMounted } from 'vue'
- export default {
- setup() {
- const keyword = ref('')
- // 1. 页面加载时读取缓存
- onMounted(() => {
- const saved = localStorage.getItem('search_keyword')
- if (saved) keyword.value = saved
- })
- // 2. 监听数据变化,实时存入缓存
- watch(keyword, (newVal) => {
- localStorage.setItem('search_keyword', newVal)
- })
- return { keyword }
- }
- }
复制代码
这里需要区分两种存储:localStorage 是永久存储,关闭浏览器再打开仍在,适合 Token、用户信息;sessionStorage 是标签页级别,关闭该标签页即清除,适合表单暂存,能避免多标签页互相干扰。
如果需求只是路由切换后返回不丢数据,而不是 F5 刷新,应使用 Keep-Alive 缓存组件:在 router-view 外层包裹 keep-alive,被缓存组件会触发 activated 和 deactivated 生命周期,数据保留在内存中。但该方案无法抵抗浏览器刷新,因为刷新会重载整个页面。
对于列表页的搜索关键词、页码等,可以放进 URL 的 query 参数,例如 ?page=1&keyword=vue,刷新后路由参数不会丢失,在 created 钩子中读取 this.$route.query 即可恢复。缺点是不适合存 Token 或大对象,URL 长度有限,敏感信息也容易泄露。
五、选型建议
综合各方案的特点:Vue2 加 Vuex 的常规 SPA 项目,优先用 vuex-persistedstate,配置量最小;Vue3 加 Pinia 用 pinia-plugin-persistedstate;小型项目或只保存单个变量,手动读写 localStorage/sessionStorage 最直接;只涉及路由切换缓存用 Keep-Alive;筛选条件、分页参数放 URL query。关键仍是两点:按需持久化,退出登录同步清理缓存。 |