Pinia 页面刷新后数据丢失怎么解决?

一、原因分析

1. 状态存储机制

Pinia 的状态存储在内存中。当你刷新页面时,浏览器会重新加载页面,导致 JavaScript 环境重新初始化,这意味着存储在内存中的数据会丢失。因为刷新操作会重新加载整个应用程序,包括重新创建 Vue 实例和 Pinia 实例,而原来存储在 Pinia store 中的数据是存储在之前的 JavaScript 运行时环境中的,一旦刷新,之前的环境被清除,数据也就消失了。

2. 没有持久化存储

Pinia 本身并不会自动将数据持久化到本地存储(如 localStorage 或 sessionStorage)或其他持久化存储介质中。如果没有使用持久化存储,那么存储在 Pinia 中的数据就只是临时存储在内存中,仅在当前页面会话期间有效。

二、解决办法

可以使用 pinia-plugin-persist 插件

1. 安装

npm install pinia-plugin-persist -D

2. 引入

store/index.ts文件中

import { createPinia } from 'pinia'
import piniaPersist from 'pinia-plugin-persist'
const store = createPinia()
store.use(piniaPersist)

3. 手动配置持久化

import { defineStore } from 'pinia';

const useMyStore = defineStore('myStore', {
  state: () => ({
    count: 0
  }),
  persist: {
    enabled: true
    // 其他配置可以去pinia-plugin-persist文档查看,https://seb-l.github.io/pinia-plugin-persist/advanced/strategies.html
  }
});

三、源码解析

核心文件 index.ts

import { PiniaPluginContext } from 'pinia'

export interface PersistStrategy {
  key?: string;
  storage?: Storage;
  paths?: string[];
}

export interface PersistOptions {
  enabled: true;
  strategies?: PersistStrategy[];
}

type Store = PiniaPluginContext['store'];
type PartialState = Partial<Store['$state']>;

declare module 'pinia' {
  export interface DefineStoreOptionsBase<S, Store> {
    persist?: PersistOptions;
  }
}

export const updateStorage = (strategy: PersistStrategy, store: Store) => {
  const storage = strategy.storage || sessionStorage
  const storeKey = strategy.key || store.$id

  if (strategy.paths) {
    const partialState = strategy.paths.reduce((finalObj, key) => {
      finalObj[key] = store.$state[key]
      return finalObj
    }, {} as PartialState)

    storage.setItem(storeKey, JSON.stringify(partialState))
  } else {
    storage.setItem(storeKey, JSON.stringify(store.$state))
  }
}

export default ({ options, store }: PiniaPluginContext): void => {
  if (options.persist?.enabled) {
    const defaultStrat: PersistStrategy[] = [{
      key: store.$id,
      storage: sessionStorage,
    }]

    const strategies = options.persist?.strategies?.length ? options.persist?.strategies : defaultStrat

    strategies.forEach((strategy) => {
      const storage = strategy.storage || sessionStorage
      const storeKey = strategy.key || store.$id
      const storageResult = storage.getItem(storeKey)

      if (storageResult) {
        store.$patch(JSON.parse(storageResult))
        updateStorage(strategy, store)
      }
    })

    store.$subscribe(() => {
      strategies.forEach((strategy) => {
        updateStorage(strategy, store)
      })
    })
  }
}

1. 导入和类型定义

import { PiniaPluginContext } from 'pinia'

export interface PersistStrategy {
  key?: string;
  storage?: Storage;
  paths?: string[];
}

export interface PersistOptions {
  enabled: true;
  strategies?: PersistStrategy[];
}

type Store = PiniaPluginContext['store'];
type PartialState = Partial<Store['$state']>;
  • 导入 PiniaPluginContext:从 pinia 库中导入 PiniaPluginContext,它包含了 Pinia 插件执行时所需的上下文信息,如 storeoptions 等。
  • PersistStrategy 接口:定义了持久化策略的类型。
    • key:可选属性,用于指定存储在 Storage 中的键名,默认为 store$id
    • storage:可选属性,指定存储的位置,类型为 Storage,可以是 localStoragesessionStorage 等,默认为 sessionStorage
    • paths:可选属性,是一个字符串数组,用于指定只持久化 store 状态中的部分属性。
  • PersistOptions 接口:定义了持久化的配置选项。
    • enabled:必须为 true,表示开启持久化功能。
    • strategies:可选属性,是一个 PersistStrategy 数组,可指定多个持久化策略。
  • 类型别名
    • Store:从 PiniaPluginContext 中提取 store 的类型。
    • PartialState:表示 store 状态的部分属性,是 Store['$state']Partial 类型。

2. 扩展 Pinia 选项接口

declare module 'pinia' {
  export interface DefineStoreOptionsBase<S, Store> {
    persist?: PersistOptions;
  }
}

通过 declare module 语法扩展 pinia 库中的 DefineStoreOptionsBase 接口,添加了一个可选的 persist 属性,类型为 PersistOptions。这样,在定义 Pinia store 时,就可以使用 persist 选项来配置持久化功能。

3. updateStorage 函数

export const updateStorage = (strategy: PersistStrategy, store: Store) => {
  const storage = strategy.storage || sessionStorage
  const storeKey = strategy.key || store.$id

  if (strategy.paths) {
    const partialState = strategy.paths.reduce((finalObj, key) => {
      finalObj[key] = store.$state[key]
      return finalObj
    }, {} as PartialState)

    storage.setItem(storeKey, JSON.stringify(partialState))
  } else {
    storage.setItem(storeKey, JSON.stringify(store.$state))
  }
}
  • 功能:该函数用于将 store 的状态存储到指定的 Storage 中。
  • 参数
    • strategy:持久化策略对象,包含存储的键名、存储位置和要持久化的属性路径等信息。
    • store:Pinia store 实例。
  • 逻辑
    • 首先,根据 strategy 中的 storage 属性获取存储位置,若未指定则使用 sessionStorage;根据 strategy 中的 key 属性获取存储键名,若未指定则使用 store$id
    • strategy 中指定了 paths,则只提取 store 状态中 paths 所包含的属性,将其存储到 partialState 对象中,然后将 partialState 序列化为 JSON 字符串并存储到 Storage 中。
    • 若未指定 paths,则将整个 store 状态序列化为 JSON 字符串并存储到 Storage 中。

4. 插件主函数

export default ({ options, store }: PiniaPluginContext): void => {
  if (options.persist?.enabled) {
    const defaultStrat: PersistStrategy[] = [{
      key: store.$id,
      storage: sessionStorage,
    }]

    const strategies = options.persist?.strategies?.length ? options.persist?.strategies : defaultStrat

    strategies.forEach((strategy) => {
      const storage = strategy.storage || sessionStorage
      const storeKey = strategy.key || store.$id
      const storageResult = storage.getItem(storeKey)

      if (storageResult) {
        store.$patch(JSON.parse(storageResult))
        updateStorage(strategy, store)
      }
    })

    store.$subscribe(() => {
      strategies.forEach((strategy) => {
        updateStorage(strategy, store)
      })
    })
  }
}
  • 功能:这是 Pinia 插件的主函数,用于实现持久化功能。
  • 参数:接收一个 PiniaPluginContext 对象,包含 options(store 的配置选项)和 store(Pinia store 实例)。
  • 逻辑
    • 首先检查 options.persist 是否存在且 enabledtrue,若不满足则不执行持久化操作。
    • 定义一个默认的持久化策略 defaultStrat,使用 store$id 作为键名,sessionStorage 作为存储位置。
    • 根据 options.persist.strategies 是否存在来确定最终使用的持久化策略数组 strategies,若不存在则使用默认策略。
    • 遍历 strategies 数组,对于每个策略:
      • Storage 中获取存储的状态信息。
      • 若获取到存储结果,则使用 store.$patch 方法将存储的状态应用到 store 中,并调用 updateStorage 函数更新存储。
    • 使用 store.$subscribe 方法监听 store 的状态变化,当状态发生变化时,遍历 strategies 数组,调用 updateStorage 函数将最新状态存储到 Storage 中。

总结

这段代码实现了一个 Pinia 插件,用于将 Pinia store 的状态持久化到 Storage 中。它支持多种持久化策略,可以指定存储的键名、存储位置和要持久化的属性路径。在应用启动时,会尝试从 Storage 中恢复状态;在状态发生变化时,会自动更新存储。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值