Skip to content

Pinia 状态管理

前置说明

状态管理采用官方 Pinia v3.0.4 单文件版,适配 uni-app-x 蒸汽 JS 引擎,并去掉了 @vue/devtools-api。 具体位置在 uni_modules/x-pinia-s/js_sdk/pinia.js。另外提供了持久化插件 piniaPersist.ts,用 uni.setStorageSync 保存 store.$state,下次启动自动还原。

演示见 pages/store/xPiniaStore,跨页读取见 pages/store/storeChildPage。业务登录态见 store/useUserStore.uts


安装 Pinia

ts

import App from './App.uvue'
import { createSSRApp } from 'vue'
import Xui from "@/uni_modules/tmx-ui"
// @ts-ignore
import { createPinia } from '@/uni_modules/x-pinia-s/js_sdk/pinia'
import { createPersistPlugin } from '@/uni_modules/x-pinia-s/js_sdk/piniaPersist'

const pinia = createPinia()

export function createApp() {
	// @ts-ignore
	const app = createSSRApp(App)

	pinia.use(createPersistPlugin({
		keyPrefix: 'pinia:',
		includeStores: ['counter', 'user'],
		excludeStores: [],
		serializer: null
	}))
	app.use(pinia)
	app.use(Xui)

	return {
		app
	}
}

includeStores 是白名单,只有写进去的 store.$id 才会持久化。当前演示把 counteruser 加进去了。excludeStores 是黑名单,同时命中时黑名单优先。includeStoresnull 表示全部持久化。

定义 Store

推荐 Setup 写法。defineStore 的第一个参数是唯一 id,持久化白名单按这个 id 匹配。

演示页用的是 pages/store/useInfo.uts

ts

import { defineStore } from '@/uni_modules/x-pinia-s/js_sdk/pinia'
export const useCounterStore = defineStore('counter', () => {
	const count = ref<number>(0)
	const name = ref<string>('')
	const doubled = computed(() : number => count.value * 2)

	const increment = () => {
		count.value++
	}

	const setName = (n : string) => {
		name.value = n
	}

	const reset = () => {
		count.value = 0
		name.value = ''
	}

	return {
		count,
		name,
		doubled,
		increment,
		setName,
		reset
	}
})

请求库用的登录态在 store/useUserStore.uts,id 为 user

ts

import { ref, computed } from 'vue'
import { defineStore } from '@/uni_modules/x-pinia-s/js_sdk/pinia'
import type { UseInfo } from '@/pages/request/interface'

export const useUserStore = defineStore('user', () => {
	const token = ref<string | null>(null)
	const user = ref<UseInfo>({
		naicename: '',
		avatar: '',
		id: '',
		tags: [] as string[],
		level: -1
	} as UseInfo)

	const isLogin = computed(() : boolean => token.value != null && token.value != '')

	const setLogin = (newToken : string | null, userInfo : UseInfo | null) => {
		token.value = newToken
		if (userInfo != null) {
			user.value = userInfo!
		}
	}

	const loginOut = () => {
		token.value = null
		user.value = {
			naicename: '',
			avatar: '',
			id: '',
			tags: [] as string[],
			level: -1
		} as UseInfo
	}

	return {
		token,
		user,
		isLogin,
		setLogin,
		loginOut
	}
})

在任意 Uts 中使用

uts

import { useUserStore } from "@/store/useUserStore.uts"
const userStore = useUserStore()
console.log(userStore.token)
console.log(userStore.isLogin)
userStore.setLogin('demo-token-888', null)

在 Uvue 内使用

1.组合式模板内

store 本身是响应式的,模板里可以直接绑 store.xxx,也可以调 action。

ts

import { useCounterStore } from "./useInfo.uts"
const store = useCounterStore()
store.increment()

uts 内导入后可以在模板内使用

vue

<x-stepper v-model="store.count"></x-stepper>
<x-text>{{store.doubled}}</x-text>
<x-button @click="store.increment()">计数叠加</x-button>
<x-input v-model="(store.name as string)"></x-input>

2.跨页面解构,保持响应式

直接 const { count } = store 会丢掉响应式。跨页或要拆字段时用 storeToRefs

ts

import { storeToRefs } from '@/uni_modules/x-pinia-s/js_sdk/pinia'
import { useCounterStore } from "./useInfo.uts"
const store = useCounterStore()
const { count, name, doubled } = storeToRefs(store)
vue

<x-text>count:{{count}}</x-text>
<x-text>computed: {{doubled}}</x-text>
<x-text>name: {{name}}</x-text>

演示子页是 pages/store/storeChildPage,和父页共用同一个 counter store。

订阅状态变化

ts

import { useCounterStore } from "./useInfo.uts"
const store = useCounterStore()
store.$subscribe((m, state) => {
	console.log(m, state)
})

持久化

挂上 createPersistPlugin 后,白名单里的 store 每次 $state 变化都会写入本地。关闭应用再打开,会还原成上次的值。

演示页改完 count / name 后关掉 App,再进 /pages/store/xPiniaStore 就能看到恢复结果。点重置只清内存,若该 store 在白名单里,下一次变化仍会写回存储。

只清存储、不影响内存状态:

ts

import { clearPersistedState } from '@/uni_modules/x-pinia-s/js_sdk/piniaPersist'
clearPersistedState('counter', 'pinia:')

Pinia 方法

如果想看完整类型,见插件目录 uni_modules/x-pinia-s/js_sdk/pinia.d.ts

名称说明
createPinia创建 Pinia 实例,在 main.utsapp.use(pinia)
defineStore定义 store,支持 options 与 setup 两种写法
storeToRefs解构 state / getters 并保持响应式
mapState / mapActions / mapStores / mapWritableStateOptions API 辅助函数
setActivePinia / getActivePinia组件外或 SSR 时切换活动实例
disposePinia销毁 Pinia 实例
createPersistPlugin持久化插件
clearPersistedState清除某个 store 的持久化数据

持久化插件参数

ts

export type PersistSerializer = {
	serialize : (state : any) => string,
	deserialize : (raw : string) => any
}

export type PersistOptions = {
	/** storage key 前缀,默认 pinia: */
	keyPrefix : string,
	/** 白名单;null 表示全部持久化 */
	includeStores : string[] | null,
	/** 黑名单,优先级高于 includeStores */
	excludeStores : string[],
	/** 自定义序列化器;null 使用 JSON */
	serializer : PersistSerializer | null
}

实际存储 key 为 keyPrefix + store.$id,例如 pinia:counterpinia:user

更多扩展

官方文档见 pinia.vuejs.org。本仓库用的是单文件移植版,API 与官方 Setup Store 一致。后续若要升级,替换 uni_modules/x-pinia-s/js_sdk/pinia.js 即可,持久化插件可继续用。

最近更新