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.0v0.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 actionstore.$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)——示例工程云打包产物(蒸汽模式),安装后进入各功能页即可在真机运行全部用例。

六、文档与资源

相关链接

欢迎反馈与共建:GitHub

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐