uni-app中应安装@dcloudio/pinia(HBuilderX≥4.14)或pinia@2.0.36(旧版/H5),持久化须用pinia-plugin-unistorage并手动配置uni存储适配器,store初始化必须在createSSRApp后、app.use前且不可被条件编译包裹。

Pinia 是 uni-app(Vue 3)项目中事实上的全局状态管理首选,但直接照搬 Vue 项目配置会踩坑——尤其是跨端兼容性、持久化、HBuilderX 版本适配这三块,稍不注意就白忙活。
uni-app 中该装哪个 pinia 包?
pinia 包?别装官方 pinia,也别装 pinia-plugin-persistedstate
✅ 正确做法是:
- HBuilderX ≥ 4.14:直接运行
npm install pinia(不加版本号) - HBuilderX < 4.14 或 CLI 项目:必须固定为
pinia@2.0.36(高版本在旧基座上会报Cannot read property 'isMounted' of undefined) - 持久化插件必须用
pinia-plugin-unistorage,不是pinia-plugin-persistedstate - 安装命令:
npm install pinia-plugin-unistorage
它内部自动调用uni.setStorageSync/uni.getStorageSync,天然支持小程序、App、H5 三端
main.js 里初始化 Pinia 的时机和写法
Pinia 的时机和写法错在“先 createApp,再 app.use(store)”,但漏了关键条件判断
✅ 正确顺序和包裹逻辑:
- 必须在
createSSRApp或createApp之后、app.use(...)之前创建 store 实例 -
store初始化代码不能被/<em> #ifdef MP-WEIXIN </em>/等条件编译包裹(JS 模块导入不走条件编译) - 持久化插件注册必须显式传入 storage 适配器:
import { createSSRApp } from 'vue'
import { createPinia } from 'pinia'
import piniaPluginUnistorage from 'pinia-plugin-unistorage'
<p>export function createApp() {
const app = createSSRApp(App)
const pinia = createPinia()
pinia.use(piniaPluginUnistorage({
storage: {
getItem: key => uni.getStorageSync(key),
setItem: (key, value) => uni.setStorageSync(key, value),
removeItem: key => uni.removeStorageSync(key)
}
}))
app.use(pinia)
return { app }
}⚠️ 常见错误:把 pinia.use(...) 写在 app.use(pinia) 之后 → 插件不会生效
如何让某个 store 自动持久化?
不是所有 state 都需要存本地,比如临时弹窗开关、loading 状态就不该进 storage
✅ 在定义 store 时,用 persist 选项精准控制:
import { defineStore } from 'pinia'
<p>export const useUserStore = defineStore('user', {
state: () => ({
token: '',
userInfo: {},
theme: 'light'
}),
persist: {
// 只持久化 token 和 theme,userInfo 不存(可能含敏感字段或体积大)
paths: ['token', 'theme']
}
})⚠️ 注意:
-
persist是pinia-plugin-unistorage提供的选项,不是 Pinia 原生能力 - 如果没生效,检查是否漏了
pinia.use(...),或路径名拼错(区分大小写) - 小程序端首次启动时,
uni.getStorageSync可能返回null,插件会自动跳过反序列化,不会报错
为什么 useStore() 在某些页面里取不到最新值?
useStore() 在某些页面里取不到最新值?常见于 Tab 页面、自定义组件、或使用了 onLoad 生命周期的页面
✅ 根本原因是:Pinia store 是响应式的,但 uni-app 的页面实例生命周期和 Vue 组件不完全对齐
- 不要在
onLoad里直接读store.xxx,应改用computed或watch - Tab 页面切换时,页面可能被缓存,
onShow中可主动触发store.$patch或重新 fetch - 若 store 数据来自异步(如登录后赋值),确保调用
store.$hydrate()(插件自动调用,但手动触发更稳妥)
最简验证方式:
import { useUserStore } from '@/stores/user'
export default {
setup() {
const userStore = useUserStore()
// ✅ 正确:用 computed 包一层,确保响应
const token = computed(() => userStore.token)
return { token }
}
}跨端状态同步本身不难,难的是「什么时候读」「什么时候存」「哪部分该存」——这些细节不抠清楚,上线后用户一杀进程,购物车就空,主题色就重置,问题全在看不见的地方。


















