UniApp 中 Pinia 状态管理的落地与持久化方案

在 UniApp 跨端开发中,状态管理一直是一个绕不开的话题。早期 Vuex 是官方推荐方案,但随着 Vue3 生态的成熟,Pinia 凭借更简洁的 API、更好的 TypeScript 支持和模块化设计,逐渐成为主流选择。本文将带你从零开始,在 UniApp 中落地 Pinia,并重点解决多端环境下的状态持久化问题。

为什么在 UniApp 中选择 Pinia

Pinia 是 Vue 官方团队推荐的新一代状态管理库,相比 Vuex 有以下几个明显优势:

  • 更简洁的 API:去掉了 mutation,只有 state、getter 和 action,写法更接近组合式函数。
  • 完整的 TypeScript 支持:类型推断友好,无需额外配置。
  • 模块化天然支持:每个 store 独立定义,无需嵌套模块。
  • 体积更小:压缩后仅约 1KB 左右,对小程序包体积友好。

在 UniApp 中,无论是 H5、小程序还是 App 端,Pinia 都能良好运行。尤其是 UniApp 对 Vue3 的支持已经非常稳定,使用 Pinia 几乎没有额外成本。

环境准备与安装

首先确保你的 UniApp 项目基于 Vue3 创建。如果是 HBuilderX 创建的项目,选择 Vue3 版本即可。然后安装 Pinia:

npm install pinia
# 或者
yarn add pinia

如果你使用的是 UniApp 的 Vite 版本,Pinia 可以直接使用;如果是 webpack 版本,建议升级到 Vue3 + Vite 模板以获得更好的开发体验。

在 UniApp 中注册 Pinia

main.jsmain.ts 中注册 Pinia:

import { createSSRApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'

export function createApp() {
  const app = createSSRApp(App)
  const pinia = createPinia()
  app.use(pinia)
  return { app }
}

注意 UniApp 的入口文件导出的是 createApp 函数,而不是直接创建 app 实例。这是 UniApp 为了兼容多端渲染所做的封装,Pinia 的注册方式与普通 Vue3 项目一致。

定义一个 Store

stores 目录下创建 user.js

import { defineStore } from 'pinia'

export const useUserStore = defineStore('user', {
  state: () => ({
    token: '',
    userInfo: null,
    theme: 'light'
  }),
  getters: {
    isLogin: (state) => !!state.token
  },
  actions: {
    setToken(token) {
      this.token = token
    },
    setUserInfo(info) {
      this.userInfo = info
    },
    logout() {
      this.token = ''
      this.userInfo = null
    }
  }
})

在组件中使用:

<script setup>
import { useUserStore } from '@/stores/user'
const userStore = useUserStore()
</script>

<template>
  <view>{{ userStore.isLogin ? '已登录' : '未登录' }}</view>
</template>

持久化方案的必要性

Pinia 的状态默认保存在内存中,页面刷新或小程序冷启动后会丢失。对于 token、用户信息、主题设置等数据,必须做持久化。UniApp 提供了 uni.setStorageSyncuni.getStorageSync,可以跨端使用,这是持久化的基础。

常见的持久化思路有两种:

  1. 手动持久化:在 action 中手动写入和读取 storage。
  2. 插件自动持久化:使用 pinia-plugin-persistedstate 或自定义插件。

手动方式简单但重复代码多,推荐使用插件方案。

使用 pinia-plugin-persistedstate

安装插件:

npm install pinia-plugin-persistedstate

main.js 中注册:

import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'

const pinia = createPinia()
pinia.use(piniaPluginPersistedstate)

然后在 store 中配置 persist

export const useUserStore = defineStore('user', {
  state: () => ({
    token: '',
    userInfo: null
  }),
  persist: {
    key: 'user-store',
    storage: {
      getItem: (key) => uni.getStorageSync(key),
      setItem: (key, value) => uni.setStorageSync(key, value)
    },
    paths: ['token', 'userInfo']
  }
})

这里的关键是自定义 storage,因为默认插件使用 localStorage,在小程序和 App 端不存在。通过传入 uni.getStorageSyncuni.setStorageSync,即可实现多端兼容。

自定义持久化插件的实现

如果你不想引入额外依赖,也可以自己写一个简单的持久化插件:

export function persistPlugin({ store, options }) {
  const persist = options.persist
  if (!persist) return

  const key = persist.key || store.$id
  const paths = persist.paths || Object.keys(store.$state)

  // 恢复
  const saved = uni.getStorageSync(key)
  if (saved) {
    try {
      const data = JSON.parse(saved)
      const patch = {}
      paths.forEach((p) => {
        if (data[p] !== undefined) patch[p] = data[p]
      })
      store.$patch(patch)
    } catch (e) {
      console.error('持久化恢复失败', e)
    }
  }

  // 订阅变化
  store.$subscribe((_mutation, state) => {
    const data = {}
    paths.forEach((p) => {
      data[p] = state[p]
    })
    uni.setStorageSync(key, JSON.stringify(data))
  })
}

注册方式:

pinia.use(persistPlugin)

这个插件逻辑清晰,适合对包体积敏感或需要深度定制的项目。

多端注意事项

  • 小程序:storage 有 10MB 上限,避免存储大对象。建议只持久化必要字段。
  • App 端uni.setStorageSync 是同步的,频繁写入可能影响性能,可考虑防抖。
  • H5 端uni.setStorageSync 底层使用 localStorage,注意同源策略。
  • SSR:如果使用 UniApp 的 SSR 模式,需要在客户端挂载后再恢复状态,避免服务端读取不到 storage。

总结

在 UniApp 中落地 Pinia 并不复杂,核心在于两点:正确注册 Pinia 实例解决多端持久化。通过 pinia-plugin-persistedstate 配合 UniApp 的 storage API,可以快速实现跨端持久化;如果追求轻量,自定义插件也是不错的选择。建议根据项目规模选择方案,并在持久化时注意字段裁剪和性能优化,让状态管理真正服务于业务,而不是成为负担。

未经允许不得转载:任鹏个人博客 » UniApp 中 Pinia 状态管理的落地与持久化方案

赞 (0) 打赏

评论 0

取消
  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏