UniApp + Pinia 数据持久化

75 阅读4分钟

UniApp + Pinia 数据持久化:一个高性能的读写分离方案

在 UniApp 开发中,使用 Pinia 进行状态管理已经非常普及。随着 App 复杂度增加,我们经常需要把一些关键数据(如 Token、用户信息、设置项)存储在本地,以便下次打开 App 时能直接恢复。

但是,在 UniApp(特别是小程序)环境下做持久化,往往会遇到几个棘手的问题:

  • 启动体验差:如果异步读取数据,App 启动时界面会先显示“初始值”(比如空白或未登录状态),过几百毫秒后才闪变为“真实数据”。
  • 操作卡顿:如果在主线程同步写入大量数据,用户滑动页面时可能会感觉到掉帧。
  • 数据类型限制JSON.stringify 原生不支持 DateBigInt 等类型,存进去取出来还得手动转换。

为了解决这些问题,我开发了一个轻量级的插件:pinia-plugin-uni-persist-next。它专注于解决 UniApp 环境下的特殊场景,提供更流畅的开发体验。


🌟 核心特性

这个插件的设计思路主要围绕着 “性能”“易用性” 展开:

  1. ⚡ 读写分离策略

    • 初始恢复(同步读):插件使用 uni.getStorageSync 同步读取数据。这保证了在组件挂载前,Store 的状态已经是最终结果,避免了界面闪烁
    • 状态保存(异步写):当状态发生变化时,插件默认使用 uni.setStorage 异步保存,并利用 post 模式延迟执行,避免频繁 IO 阻塞主线程
  2. 🛡️ 增强的序列化支持

    • 开发中难免会用到特殊类型。本插件内置了自定义序列化方法,支持以下类型的自动转换:
      • Date:自动还原为日期对象。
      • BigInt:自动转存,不报错。
      • 循环引用:能安全处理对象间的循环引用,防止程序崩溃。
  3. ⚖️ 存储体积监测

    • 考虑到小程序单条缓存通常限制为 1MB,插件会在非生产环境下监测存储大小。如果单条数据超过 3.5MB,会在控制台输出警告,帮助开发者及早发现隐患。
  4. 🧩 灵活配置

    • 支持按需持久化(paths 过滤)。
    • 支持自定义存储 Key 和前缀。
    • 支持 TypeScript 类型提示。


📦 安装

推荐使用 pnpm:

pnpm add pinia-plugin-uni-persist-next

或者 yarn/npm:

yarn add pinia-plugin-uni-persist-next
# npm install pinia-plugin-uni-persist-next

🛠️ 快速上手

1. 注册插件

在你的 main.tsindex.ts 中引入并安装插件:

import { createSSRApp } from "vue";
import { createPinia } from "pinia";
import { createUniPersistPlugin } from "pinia-plugin-uni-persist-next";
import App from "./App.vue";

export function createApp() {
  const app = createSSRApp(App);
  const pinia = createPinia();

  // 1. 创建插件实例
  const uniPersist = createUniPersistPlugin({
    keyPrefix: "my_app_", // 可选:给所有 key 加上统一前缀
  });

  // 1. 注册到 pinia
  pinia.use(uniPersist);

  app.use(pinia);
  return { app, Pinia: pinia };
}

2. 在 Store 中开启

就像按开关一样简单,在 Store 里加这几行代码就行:

import { defineStore } from "pinia";

export const useUserStore = defineStore("user", {
  state: () => ({
    token: "",
    userInfo: { name: "海绵宝宝", age: 10 },
    loginTime: new Date(), // 直接存 Date 类型!
  }),
  actions: {
    setToken(token: string) {
      this.token = token;
    },
  },
  // ✨ 此处开启持久化
  persist: {
    enabled: true,
  },
});

搞定!现在就算你刷新页面,或者杀掉 App 重启,token 依然在,而且 loginTime 取出来还是 Date 对象,不用你自己转。


⚙️ 进阶用法(满足你的控制欲)

场景一:数据太多,我只想存一部分

比如 userInfo 很长,我只想存个 token

persist: {
  enabled: true,
  strategies: [
    {
      paths: ['token'], // 指定名单:只存 token
    },
  ],
}

场景二:我想换个存储 Key

默认 Key 是 Store 的 ID(比如上面的 user),怕跟别人的 key 撞车?改它:

persist: {
  enabled: true,
  strategies: [
    {
      key: 'my_auth_cache', // 最终在 Storage 里就是 "my_auth_cache"
      paths: ['token'],
    },
  ],
}

🔍 技术原理(给好奇宝宝)

为什么它比 JSON.stringify 更强?

原生的 JSON.stringify 很傻,遇到 Date 变成字符串,遇到 BigInt 报错,遇到循环引用直接崩。

本插件内部封装了一套 safeStringify

  • 遇到 Date -> 标记为日期 -> 读取时自动变回 Date
  • 遇到 BigInt -> 转字符串存 -> 保证不报错
  • 遇到 循环引用 -> 自动切断 -> 保证不崩溃

这意味着:你可以放心地把任何数据往 Pinia 里丢,剩下的交给插件。


📝 总结

pinia-plugin-uni-persist-next 就像是给你的 UniApp 加上了一个稳固的后勤补给站

  • 很轻:不占什么包体积。
  • 很强:Date、BigInt、循环引用统统搞定。
  • 很滑:读写分离设计,绝不拖慢 UI 线程。

如果你还在为 UniApp 的数据持久化头疼,或者忍受着旧方案的卡顿,不妨试试这个新方案!

🔗 GitHub 地址: github.com/Hollelihanq…
觉得好用的话,求个 Star ⭐️!你的支持是我更新的动力!