v0.2.0 为重大架构升级:store 定义改为 class + reactive(
class XxxStore extends PiniaStoreBase),state 用reactive<T>强类型包裹、派生值用 class getter、action 为普通 method——由此换来完整实例 API($patch/$reset/$subscribe/$onAction/$dispose/$id/$state/$save)与内置持久化插件(createPersistPlugin:store 创建自动恢复、$patch/$reset/$save自动落盘)。⚠️ 破坏性变更:0.1.0 setup 式 API(storeToRefs/ 顶层patch/loadPersisted等)已移除,迁移对照见第四节。
一、为什么升级架构
v0.1.0 的 setup 式 + Ref 形态跑通了下限,但也撞上了天花板——两条平台硬约束:
- UTS 无 Proxy、无反射:store 对象不可动态挂方法、不可运行时枚举字段,
$patch/$subscribe/$onAction/$reset在 setup 式形态下全部无法实现(v0.1.0 只能裁掉) - 新版 HBuilderX 编译器对 UTS 插件内运行时类 vue 符号(
Ref/watch/effectScope)的 Kotlin import 生成失效——云打包 Kotlin 编译直接 Unresolved
而reactive<T>在 UTS 中是编译期展开(生成属性代理类,getter/setter 内联依赖追踪),不受上述约束影响。class + reactive 因此成为 uni-app x 插件实现完整 Pinia 功能面的唯一可行架构。v0.2.0 完成架构转向,一次补齐:
| 能力 | v0.1.0 | v0.2.0 |
|---|---|---|
$patch/$reset | 顶层patch(mutator)/ ❌ | ✅$patch(partial)/$reset()(_doReset钩子) |
$subscribe/$onAction | ❌(字段级watch替代) | ✅ 内置(快照分发 /callAction追踪) |
$id/$state/$dispose | 顶层函数映射 | ✅ 实例属性 / 方法 |
$save显式保存点 | — | ✅ |
| 持久化 | ❌ | ✅ 内置插件,零胶水 |
二、核心能力
class 式 defineStore
import { reactive } from 'vue' import { defineStore, PiniaStoreBase } from '@/uni_modules/ux-pinia' export type CounterState = { count: number name: string } export class CounterStore extends PiniaStoreBase { // state:reactive<T> 强类型包裹(字段为原始值,模板 / 脚本直接访问) state: CounterState = reactive<CounterState>({ count: 0, name: 'pinia' }) // 派生值:class getter(每次求值,模板经 reactive 属性依赖收集自动更新) get doubleCount(): number { return this.state.count * 2 } // constructor:super() 后必须 bindState(this.state)($-API 与持久化的前提) constructor() { super() this.bindState(this.state) } // actions = 普通 class method,直接修改 state increment(): void { this.state.count++ } } export const useCounterStore = defineStore<CounterStore>('counter', (): CounterStore => new CounterStore())结构对齐 Pinia 官方state / getters / actions心智;模板事件绑定 class method 必须带括号(@click="counter.increment()",Kotlin 端 method 不是值)。
完整实例 API($-methods)
const counter = useCounterStore() counter.$patch({ count: 10 } as UTSJSONObject) // 批量修改(经 _hydrate 钩子装载) counter.$reset() // 重置(_doReset 钩子) const unsub = counter.$subscribe((type: string, state: UTSJSONObject): void => { // type = 'patch' | 'reset' | 'save';state = _serialize() 快照 }) counter.$onAction((info: ActionInfo): void => { /* action 追踪 */ }) counter.$save() // 显式保存点(direct 修改 state 后的落盘触发) counter.$dispose() // 注销,下次 useStore() 重建 counter.$id // 'counter'$subscribe仅由$patch/$reset/$save触发(UTS 无全局订阅钩子);direct 修改 state 后的关键操作请调用counter.$save()。
内置持久化插件(零胶水)
main.uts顶层登记一次,全部 store 生效:
import { createPinia, setActivePinia, createPersistPlugin } from '@/uni_modules/ux-pinia' setActivePinia(createPinia().use(createPersistPlugin({ keyPrefix: 'ux-pinia:', includeStores: null, // null = 全部 store excludeStores: new Array<string>(), serializer: null // null = JSON 序列化 })))需要持久化的 store 重写两个钩子(Template Method 契约):
override _serialize(): UTSJSONObject { // 落盘快照:只含需持久化的字段 return { token: this.state.token } as UTSJSONObject } override _hydrate(data: UTSJSONObject): void { // 恢复回填:字段级判空(存储损坏 / 字段缺失 → 保留初始值) if (data['token'] != null) { this.state.token = data['token'] as string } }恢复在 store 创建时自动发生;落盘由$patch/$reset/$save自动触发。对比旧版「每字段两行 ref / watch 胶水」的方案,持久化成本降为两个声明式钩子。存储后端可经setStorageAdapter替换(默认 uni storage,可换 unix-storage / 原生 preferences);clearPersistedState(storeId, keyPrefix)可清空单个 store。
免挂载单例与插件体系(一如既往)
模块级注册表保证应用级单例,跨页面共享状态,无需app.use(实测原生端app.use不触发 install,免挂载是唯一双端一致的形态);插件pinia.use(plugin)链式注册,PiniaPluginContext(pinia/store/storeId)对齐标准 Pinia——持久化插件本身就是插件体系的第一个内置成员。
三、平台支持现状
| 平台 | 状态 |
|---|---|
| Web | ✅ 全用例通过(基础用法 / 响应式 / 单例跨页面 / 实例 API / 跨 store / 订阅 / 插件 / 持久化) |
| Android | ✅ 蒸汽模式(vapor)云打包 Kotlin 编译链通过,真机用例复验中 |
| iOS / 鸿蒙 | ⏳ 代码全端同构(纯 UTS、无平台分支),待实测 |
关键实测裁决(驱动本次架构定稿):reactive<T>为编译期展开(Kotlin 生成正常);运行时类 vue 符号(Ref/ref/watch/effectScope)在插件内 Kotlin import 生成失效(effectScope被生成器静默丢弃——派生值因此用 getter 而非 computed);模板 class method 绑定必须带括号;Kotlin 泛型擦除边界(error17)在库内收敛,业务侧零感知。
四、0.1.0 → 0.2.0 迁移对照
| v0.1.0(setup 式,已移除) | v0.2.0(class 式) |
|---|---|
type CounterStore = { count: Ref<number>, ... }+ setup 函数 | class CounterStore extends PiniaStoreBase(state =reactive<T>字段) |
脚本store.count.value/ 模板storeToRefs解构 | store.state.count直接读写,脚本模板同路径 |
patch(store, fn) | store.$patch(partial)或直接调用 action |
字段级watch(store.count, cb) | store.$subscribe(cb) |
| 自定义 reset action | store.$reset()(_doReset钩子) |
loadPersisted/savePersisted+ 每字段 ref / watch 胶水 | createPersistPlugin+_serialize/_hydrate双钩子 |
getStoreId(store)/dispose(store) | store.$id/store.$dispose()(顶层函数仍保留) |
五、快速上手
pnpmadd@meng-xi/unix-piniamkdir-puni_modules&&cp-Rnode_modules/@meng-xi/unix-pinia uni_modules/ux-pinia插件市场搜索ux-pinia导入亦可。定义 store 见第二节 class 式示例;页面直接消费counter.state.count与 getter。不想搭工程?安卓真机可直接下载体验 App:下载体验 App(APK)——示例工程云打包产物(蒸汽模式),安装后进入各功能页即可在真机运行全部用例。
六、文档与资源
- 📖 完整文档(指南 9 篇 + API 3 篇,中英双语,从入门到精通)
- 📝 更新日志
相关链接
- 示例工程 packages/playground(首页分组导航 + 9 个功能验证页,HBuilderX 发行 Web / 云打包验证)
- API 参考
欢迎反馈与共建:GitHub