v0.2.0 架构升级:class + reactive 重塑状态管理
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()(顶层函数仍保留) |
五、快速上手
pnpm add @meng-xi/unix-pinia
mkdir -p uni_modules && cp -R node_modules/@meng-xi/unix-pinia uni_modules/ux-pinia
插件市场搜索 ux-pinia 导入亦可。定义 store 见第二节 class 式示例;页面直接消费 counter.state.count 与 getter。不想搭工程?安卓真机可直接下载体验 App:下载体验 App(APK)——示例工程云打包产物(蒸汽模式),安装后进入各功能页即可在真机运行全部用例。
六、文档与资源
相关链接
- 示例工程 packages/playground(首页分组导航 + 9 个功能验证页,HBuilderX 发行 Web / 云打包验证)
- API 参考
欢迎反馈与共建:GitHub
更多推荐


所有评论(0)