Skip to content
On this page

Pinia 状态持久化

Pinia 本身不提供状态持久化功能,但可以通过插件或自定义实现来将状态保存到本地存储中。本指南将详细介绍如何实现 Pinia 状态的持久化。

使用 pinia-plugin-persistedstate 插件

这是最常用的持久化解决方案。

安装插件

bash
npm install pinia-plugin-persistedstate

基本配置

javascript
// main.js 或 main.ts
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')

在 Store 中启用持久化

javascript
import { defineStore } from 'pinia'

export const useUserStore = defineStore('user', {
  state: () => ({
    name: '',
    email: '',
    preferences: {
      theme: 'light',
      language: 'en',
    },
  }),
  
  // 启用持久化
  persist: true, // 简单持久化,使用默认设置
})

高级持久化配置

javascript
import { defineStore } from 'pinia'

export const useUserStore = defineStore('user', {
  state: () => ({
    name: '',
    email: '',
    preferences: {
      theme: 'light',
      language: 'en',
    },
    loginCount: 0,
  }),
  
  persist: {
    // 自定义存储键名
    key: 'user-store',
    
    // 选择要持久化的状态字段
    pick: ['name', 'preferences'], // 只持久化 name 和 preferences 字段
    
    // 排除某些字段不持久化
    // omit: ['loginCount'], // 不持久化 loginCount 字段
    
    // 自定义存储方式
    storage: localStorage, // 默认为 localStorage
    
    // 自定义序列化函数
    serializer: {
      serialize: JSON.stringify,
      deserialize: JSON.parse,
    },
  },
})

多存储支持

使用 sessionStorage

javascript
export const useTempStore = defineStore('temp', {
  state: () => ({
    temporaryData: null,
    sessionInfo: {},
  }),
  
  persist: {
    storage: sessionStorage, // 使用 sessionStorage,页面关闭后数据丢失
  },
})

自定义存储实现

javascript
// 自定义存储适配器
const customStorage = {
  getItem(key) {
    // 自定义获取逻辑
    return localStorage.getItem(key)
  },
  
  setItem(key, value) {
    // 自定义设置逻辑
    localStorage.setItem(key, value)
  },
  
  removeItem(key) {
    // 自定义删除逻辑
    localStorage.removeItem(key)
  },
}

export const useCustomStore = defineStore('custom', {
  state: () => ({
    data: {},
  }),
  
  persist: {
    storage: customStorage,
  },
})

复杂持久化场景

分割持久化配置

javascript
export const useAppStore = defineStore('app', {
  state: () => ({
    user: null,
    settings: {
      theme: 'light',
      notifications: true,
    },
    cache: {},
    tempData: null,
  }),
  
  persist: [
    {
      // 用户信息使用 localStorage 持久化
      key: 'app-user',
      pick: ['user'],
      storage: localStorage,
    },
    {
      // 设置使用 sessionStorage 持久化
      key: 'app-settings',
      pick: ['settings'],
      storage: sessionStorage,
    },
    {
      // 缓存数据使用自定义存储
      key: 'app-cache',
      pick: ['cache'],
      storage: localStorage,
    },
  ],
})

条件持久化

javascript
export const useConditionalStore = defineStore('conditional', {
  state: () => ({
    sensitiveData: null,
    publicData: {},
    userPreferences: {},
  }),
  
  persist: computed(() => {
    const shouldPersist = localStorage.getItem('enablePersistence') === 'true'
    
    if (shouldPersist) {
      return {
        pick: ['publicData', 'userPreferences'],
        storage: localStorage,
      }
    } else {
      return false // 不启用持久化
    }
  }),
})

手动持久化实现

如果不使用插件,可以手动实现持久化:

javascript
import { defineStore } from 'pinia'

export const useManualPersistStore = defineStore('manual-persist', {
  state: () => ({
    count: 0,
    name: '',
    lastUpdated: null,
  }),
  
  actions: {
    // 初始化时从存储加载数据
    hydrate() {
      const savedState = localStorage.getItem('manual-persist-store')
      if (savedState) {
        const parsedState = JSON.parse(savedState)
        this.$patch(parsedState)
      }
    },
    
    // 手动保存到存储
    saveToStorage() {
      const stateToSave = {
        count: this.count,
        name: this.name,
        lastUpdated: this.lastUpdated,
      }
      localStorage.setItem('manual-persist-store', JSON.stringify(stateToSave))
    },
    
    increment() {
      this.count++
      this.lastUpdated = new Date().toISOString()
      this.saveToStorage() // 每次修改后保存
    },
    
    setName(newName) {
      this.name = newName
      this.lastUpdated = new Date().toISOString()
      this.saveToStorage()
    },
  },
  
  // 在 Store 初始化后自动加载数据
  hydrate: {
    afterHydrate: () => {
      // 可以在这里添加加载后的回调逻辑
    },
  },
})

// 在组件挂载时调用 hydrate
// onMounted(() => {
//   const store = useManualPersistStore()
//   store.hydrate()
// })

数据迁移和版本控制

带版本控制的持久化

javascript
export const useVersionedStore = defineStore('versioned', {
  state: () => ({
    version: 1,
    data: {},
    settings: {},
  }),
  
  persist: {
    key: 'versioned-store-v1', // 在键名中包含版本号
    
    serializer: {
      serialize: (state) => {
        // 在序列化时添加元数据
        return JSON.stringify({
          version: state.version,
          timestamp: Date.now(),
          data: state,
        })
      },
      deserialize: (value) => {
        try {
          const parsed = JSON.parse(value)
          
          // 如果版本不匹配,执行迁移逻辑
          if (parsed.version !== 1) {
            return migrateData(parsed)
          }
          
          return parsed.data
        } catch (error) {
          console.error('Failed to deserialize persisted state:', error)
          return { version: 1, data: {}, settings: {} }
        }
      },
    },
  },
})

function migrateData(oldData) {
  // 执行数据迁移逻辑
  const newData = { ...oldData }
  // 根据需要执行迁移操作
  newData.version = 1
  return newData
}

性能优化

防抖持久化

javascript
import { debounce } from 'lodash-es' // 或使用自己的防抖函数

export const useDebouncedStore = defineStore('debounced', {
  state: () => ({
    input: '',
    searchHistory: [],
  }),
  
  actions: {
    // 防抖保存函数
    saveToStorage: debounce(function() {
      const stateToSave = {
        input: this.input,
        searchHistory: this.searchHistory,
      }
      localStorage.setItem('debounced-store', JSON.stringify(stateToSave))
    }, 500), // 500ms 防抖
    
    updateInput(newInput) {
      this.input = newInput
      this.saveToStorage() // 触发防抖保存
    },
  },
})

选择性持久化

javascript
export const useSelectiveStore = defineStore('selective', {
  state: () => ({
    largeData: new Array(10000).fill(0).map((_, i) => i),
    smallData: { count: 0 },
    uiState: { expanded: false },
  }),
  
  persist: {
    // 只持久化小数据和 UI 状态,避免持久化大数据
    pick: ['smallData', 'uiState'],
  },
})

安全考虑

敏感数据处理

javascript
export const useSecureStore = defineStore('secure', {
  state: () => ({
    token: null,
    refreshToken: null,
    sensitiveInfo: null,
    publicInfo: {},
  }),
  
  persist: {
    // 只持久化非敏感数据
    pick: ['publicInfo'],
  },
  
  actions: {
    // 敏感数据手动处理
    setTokens(token, refreshToken) {
      this.token = token
      this.refreshToken = refreshToken
      // 不持久化敏感数据,但可以临时存储在内存中
    },
  },
})

调试和监控

持久化监控

javascript
const persistenceLogger = (context) => {
  const { store } = context
  
  // 监听持久化事件
  store.$subscribe((mutation, state) => {
    console.log(`Store ${store.$id} persisted:`, mutation, state)
  }, { detached: true })
}

// 使用插件
pinia.use(persistenceLogger)

通过以上方法,您可以根据应用需求选择合适的持久化策略,确保用户数据在页面刷新或应用重启后得以保留。