UniApp + Pinia 数据持久化

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:

bash 复制代码
pnpm add pinia-plugin-uni-persist-next

或者 yarn/npm:

bash 复制代码
yarn add pinia-plugin-uni-persist-next
# npm install pinia-plugin-uni-persist-next

🛠️ 快速上手

1. 注册插件

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

typescript 复制代码
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 里加这几行代码就行:

typescript 复制代码
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

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

场景二:我想换个存储 Key

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

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

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

为什么说它"读写分离"体验最好?

我们看个伪代码对比:

typescript 复制代码
// 🔵 启动 APP 时(读):必须同步!
// 如果这里用异步,页面先显示"未登录",0.1秒后变成"已登录",用户会看到界面闪烁。
// 所以插件强制使用 getStorageSync,保证页面渲染前数据就到位了。
const data = uni.getStorageSync(key);
store.$patch(data);

// 🟠 运行时(写):必须异步!
// 比如用户在滑动列表时触发了状态更新,如果这时候同步写磁盘,
// JS 线程卡住等待 IO,用户就会感觉滑动"掉帧"卡顿。
// 所以插件默认使用 setStorage(异步),并在后台悄悄保存。
store.$subscribe(() => {
  uni.setStorage({ key, data });
});

为什么它比 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 ⭐️!你的支持是我更新的动力!

相关推荐
2501_916008898 小时前
全面介绍Fiddler、Wireshark、HttpWatch、SmartSniff和firebug抓包工具功能与使用
android·ios·小程序·https·uni-app·iphone·webview
webYin8 小时前
解决 Uni-App 运行到微信小程序时 “Socket合法域名校验出错” 问题
微信小程序·小程序·uni-app
Coder_Boy_8 小时前
技术让开发更轻松的底层矛盾
java·大数据·数据库·人工智能·深度学习
helloworldandy8 小时前
使用Pandas进行数据分析:从数据清洗到可视化
jvm·数据库·python
失忆爆表症8 小时前
05_UI 组件库集成指南:Shadcn/ui + Tailwind CSS v4
前端·css·ui
小迷糊的学习记录8 小时前
Vuex 与 pinia
前端·javascript·vue.js
发现一只大呆瓜9 小时前
前端性能优化:图片懒加载的三种手写方案
前端·javascript·面试
不爱吃糖的程序媛9 小时前
Flutter 与 OpenHarmony 通信:Flutter Channel 使用指南
前端·javascript·flutter
利刃大大9 小时前
【Vue】Element-Plus快速入门 && Form && Card && Table && Tree && Dialog && Menu
前端·javascript·vue.js·element-plus
NEXT069 小时前
AI 应用工程化实战:使用 LangChain.js 编排 DeepSeek 复杂工作流
前端·javascript·langchain