状态持久化 #

为什么需要持久化? #

默认情况下,Vuex 的状态存储在内存中,页面刷新后状态会丢失。持久化可以将状态保存到本地存储中。

手动实现 #

基本实现 #

javascript
// store/index.js
import { createStore } from 'vuex'

// 从 localStorage 读取初始状态
const savedState = localStorage.getItem('vuex-state')
const initialState = savedState ? JSON.parse(savedState) : {
  user: null,
  token: null,
  settings: {}
}

const store = createStore({
  state: initialState,
  
  mutations: {
    SET_USER(state, user) {
      state.user = user
    },
    SET_TOKEN(state, token) {
      state.token = token
    }
  }
})

// 监听状态变化,保存到 localStorage
store.subscribe((mutation, state) => {
  localStorage.setItem('vuex-state', JSON.stringify(state))
})

export default store

选择性持久化 #

javascript
// 只持久化特定字段
store.subscribe((mutation, state) => {
  const persistedState = {
    user: state.user,
    token: state.token,
    settings: state.settings
    // 不保存 loading、error 等临时状态
  }
  
  localStorage.setItem('vuex-state', JSON.stringify(persistedState))
})

使用 vuex-persistedstate #

安装 #

bash
npm install vuex-persistedstate

基本使用 #

javascript
import { createStore } from 'vuex'
import createPersistedState from 'vuex-persistedstate'

const store = createStore({
  plugins: [
    createPersistedState()
  ],
  
  state: {
    user: null,
    token: null
  }
})

配置选项 #

javascript
createPersistedState({
  // 存储键名
  key: 'my-app-store',
  
  // 存储方式
  storage: window.localStorage,
  
  // 要持久化的状态路径
  paths: ['user', 'token', 'settings.theme'],
  
  // 过滤 mutation
  filter: (mutation) => {
    return !mutation.type.startsWith('temp/')
  },
  
  // 状态序列化
  reducer: (state) => ({
    user: state.user,
    token: state.token
  })
})

使用 sessionStorage #

javascript
createPersistedState({
  storage: window.sessionStorage
})

自定义存储 #

javascript
createPersistedState({
  storage: {
    getItem: (key) => {
      return JSON.parse(localStorage.getItem(key))
    },
    setItem: (key, value) => {
      localStorage.setItem(key, JSON.stringify(value))
    },
    removeItem: (key) => {
      localStorage.removeItem(key)
    }
  }
})

模块持久化 #

持久化特定模块 #

javascript
const store = createStore({
  plugins: [
    createPersistedState({
      paths: ['user', 'settings']
    })
  ],
  
  modules: {
    user: {
      namespaced: true,
      state: () => ({
        profile: null,
        token: null
      })
    },
    
    settings: {
      namespaced: true,
      state: () => ({
        theme: 'light',
        language: 'en'
      })
    },
    
    // 不持久化的模块
    notifications: {
      namespaced: true,
      state: () => ({
        items: []
      })
    }
  }
})

每个模块独立持久化 #

javascript
import { createStore } from 'vuex'
import createPersistedState from 'vuex-persistedstate'

const store = createStore({
  plugins: [
    // 用户模块持久化
    createPersistedState({
      key: 'user',
      paths: ['user']
    }),
    
    // 设置模块持久化
    createPersistedState({
      key: 'settings',
      paths: ['settings']
    })
  ]
})

加密存储 #

使用 crypto-js #

bash
npm install crypto-js
javascript
import CryptoJS from 'crypto-js'

const SECRET_KEY = 'your-secret-key'

const encryptedStorage = {
  getItem: (key) => {
    const encrypted = localStorage.getItem(key)
    if (!encrypted) return null
    
    try {
      const bytes = CryptoJS.AES.decrypt(encrypted, SECRET_KEY)
      return JSON.parse(bytes.toString(CryptoJS.enc.Utf8))
    } catch {
      return null
    }
  },
  
  setItem: (key, value) => {
    const encrypted = CryptoJS.AES.encrypt(
      JSON.stringify(value),
      SECRET_KEY
    ).toString()
    localStorage.setItem(key, encrypted)
  },
  
  removeItem: (key) => {
    localStorage.removeItem(key)
  }
}

// 使用
createPersistedState({
  storage: encryptedStorage
})

过期时间 #

实现过期机制 #

javascript
const storageWithExpiry = {
  getItem: (key) => {
    const itemStr = localStorage.getItem(key)
    if (!itemStr) return null
    
    const item = JSON.parse(itemStr)
    const now = new Date()
    
    // 检查是否过期
    if (now.getTime() > item.expiry) {
      localStorage.removeItem(key)
      return null
    }
    
    return item.value
  },
  
  setItem: (key, value) => {
    const now = new Date()
    const item = {
      value,
      expiry: now.getTime() + 24 * 60 * 60 * 1000  // 24小时过期
    }
    localStorage.setItem(key, JSON.stringify(item))
  },
  
  removeItem: (key) => {
    localStorage.removeItem(key)
  }
}

createPersistedState({
  storage: storageWithExpiry
})

实战示例 #

完整持久化方案 #

javascript
// store/plugins/persistence.js
import createPersistedState from 'vuex-persistedstate'
import CryptoJS from 'crypto-js'

const SECRET_KEY = import.meta.env.VITE_ENCRYPTION_KEY

const encryptedStorage = {
  getItem: (key) => {
    const encrypted = localStorage.getItem(key)
    if (!encrypted) return null
    
    try {
      const bytes = CryptoJS.AES.decrypt(encrypted, SECRET_KEY)
      const decrypted = bytes.toString(CryptoJS.enc.Utf8)
      const item = JSON.parse(decrypted)
      
      // 检查过期
      if (item.expiry && Date.now() > item.expiry) {
        localStorage.removeItem(key)
        return null
      }
      
      return item.value
    } catch {
      return null
    }
  },
  
  setItem: (key, value) => {
    const item = {
      value,
      expiry: Date.now() + 7 * 24 * 60 * 60 * 1000  // 7天过期
    }
    
    const encrypted = CryptoJS.AES.encrypt(
      JSON.stringify(item),
      SECRET_KEY
    ).toString()
    
    localStorage.setItem(key, encrypted)
  },
  
  removeItem: (key) => {
    localStorage.removeItem(key)
  }
}

export default createPersistedState({
  key: 'app-state',
  storage: encryptedStorage,
  paths: [
    'user.profile',
    'user.token',
    'settings.theme',
    'settings.language'
  ],
  filter: (mutation) => {
    // 不保存临时状态变更
    const excludeMutations = [
      'SET_LOADING',
      'SET_ERROR',
      'CLEAR_ERROR'
    ]
    return !excludeMutations.includes(mutation.type)
  }
})

使用插件 #

javascript
// store/index.js
import { createStore } from 'vuex'
import persistence from './plugins/persistence'
import user from './modules/user'
import settings from './modules/settings'

export default createStore({
  plugins: [persistence],
  
  modules: {
    user,
    settings
  }
})

最佳实践 #

1. 选择性持久化 #

javascript
// 只持久化必要的数据
paths: ['user.token', 'user.preferences']
// 不持久化敏感数据、临时状态

2. 加密敏感数据 #

javascript
// 敏感数据加密存储
storage: encryptedStorage

3. 设置过期时间 #

javascript
// 避免永久存储
expiry: Date.now() + 7 * 24 * 60 * 60 * 1000

4. 版本控制 #

javascript
const STORE_VERSION = '1.0.0'

const store = createStore({
  plugins: [
    createPersistedState({
      key: `app-store-v${STORE_VERSION}`
    })
  ]
})

总结 #

持久化要点:

方式 说明
手动实现 使用 subscribe + localStorage
vuex-persistedstate 推荐的插件方案
选择性持久化 只保存必要数据
加密存储 敏感数据加密
过期机制 避免永久存储

继续学习 严格模式,了解如何开启严格模式。

最后更新:2026-03-28