5.1 全局状态 Vuex 与 Pinia


5.1 全局状态 Vuex 与 Pinia

本节导读:分层地图立好后,本节进入第二层。商城的购物车要在首页、详情、购物车页三处实时同步,这正是全局状态的领地。本节对照 Vuex 与 Pinia 两条线在 uni-app 里的接入与写法,给出模块划分与持久化的工程做法,顺带划清"什么不该进 store"的边界。

从角标不同步的故障说起

上线的第一版商城有个尴尬:加购后首页角标变了,购物车页的列表却要手动刷新才多一行。根因是购物车数组各自存在两个页面的 data 里——同一份数据的两个副本注定漂移。修复方案就是第二层的引入:把购物车提升为全局状态,所有页面读同一处、改走同一套动作。这是判断"要不要进 store"的典型样本:多处关心、变更需同步,两条件齐了才进;只有一处的数据留在组件里,别让 store 变成杂物间。

Pinia 接入:三步与一个约定

Vue3 工程首选 Pinia。uni-app 里的接入比普通 Vue 项目多一个注意点——入口要用 createSSRApp 的导出形式:

// main.js import { createSSRApp } from 'vue'; import * as Pinia from 'pinia'; import { createPinia } from 'pinia'; import App from './App'; export function createApp() { const app = createSSRApp(App); app.use(createPinia()); return { app, Pinia }; // Pinia 要一并导出,部分端编译需要 }
// store/cart.js import { defineStore } from 'pinia'; export const useCartStore = defineStore('cart', { state: () => ({ items: [] // { id, name, price, num } }), getters: { count: (s) => s.items.reduce((n, g) => n + g.num, 0), total: (s) => s.items.reduce((n, g) => n + g.num * g.price, 0) }, actions: { add(goods) { const hit = this.items.find((g) => g.id === goods.id); hit ? hit.num += 1 : this.items.push({ ...goods, num: 1 }); this.persist(); }, remove(id) { this.items = this.items.filter((g) => g.id !== id); this.persist(); }, persist() { uni.setStorageSync('cart-items', this.items); }, restore() { this.items = uni.getStorageSync('cart-items') || []; } } });

组件侧的消费相当克制:const cart = useCartStore() 之后读 cart.count、调 cart.add(goods),三个页面的角标从此同源。持久化动作收在 store 内部(persist 与 restore)是本节最重要的约定——谁改状态谁落盘,业务代码永远不直接操作购物车的存储键,冷启动时在 App.vue 的 onLaunch 里调一次 restore,购物车就活过了进程重启。

Vuex 的位置与两线对照

老工程(Vue2 线)用 Vuex 是顺理成章:uni-app 对 Vuex 有内置支持,import store 后挂在原型上即可。两线的取舍不必纠结:新项目 Pinia,存量 Vuex 项目为平滑升级临时新增模块时才考虑 Vuex,且目标始终是整体迁移。写法差异上,Pinia 砍掉了 mutation 与模块命名空间两层仪式,action 直接改状态、store 按文件天然分域;Vuex 的 mutation 在多端调试工具里的时间旅行是它仅存的体验优势,但换不回天平。

维度 Pinia Vuex
心智负担 无 mutation、无命名空间 mutation 与 module 双层仪式
TypeScript 类型推导天然友好 需要额外声明补齐
uni-app 接入 main.js 导出 Pinia 即可 内置支持,挂原型
适合 Vue3 新项目 Vue2 存量工程

图 5-1 购物车状态的三页同步

图 5-1 购物车状态的三页同步

边界:这些东西别进 store

收尾把边界划清,四类常见误入:纯服务端数据的主副本(商品列表)进 store 只会制造缓存失效难题,留请求层缓存即可,store 存"当前选中"这类工作集;表单输入过程值留在组件里,提交时才进数据流;仅在单页内的 UI 状态(弹窗开关)进 store 是最常见的污染源;超大静态配置进 store 浪费内存,直接模块导出常量。store 越小越健康——它是同步的枢纽,不是数据仓库。

跨分包使用 store 的一个注意点

分包场景下 store 的使用要补一条:分包页面引用主包里的 store 模块没有问题(分包代码可以依赖主包公共块),但反过来不要把业务 store 模块放进分包——主包一旦引用分包内的模块,打包器会把该分包的代码提升进主包,分包减负的收益就被悄悄吃掉了。判断方法还是看产物:主包的公共块里若出现了本该属于分包的模块名,就说明依赖方向放反了。把"store 与公共组件只在主包,分包单向依赖主包"写进架构公约,分包结构就能长期保持清爽。

本节要点回顾

  • 进 store 的判据是"多处关心加变更需同步",两者缺一就留在组件层;
  • Pinia 接入的关键是 main.js 一并导出 Pinia;store 按业务域一文件一域;
  • 持久化内聚在 store 的 action 里,业务代码不碰存储键,冷启动 onLaunch 恢复一次;
  • 新项目 Pinia、Vue2 存量 Vuex,混合期目标是整体迁移;
  • 服务端数据主副本、表单过程值、单页 UI 状态、超大静态配置不进 store。

全局状态归位,下一节下沉到第三层:本地存储的方案选型与封装设计。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U