Pinia 是 Vue 的专属状态管理库,是 Vuex 的一个替代品,并支持组合式 API。
Pinia 是一个 Store ,它是一个保存状态和业务逻辑的实体,保存着全局状态,有点像一个永远存在的组件,每个组件都可以读取和写入它。
Pinia 有三个概念,state, getter, action
,类似于组件中的 data ,computed ,methods
。
2. 使用
使用 npm install pinia
安装,安装后将实例注册到 Vue3 应用
1 2 3 4 5 6 7 8 9 10
| import { createApp } from 'vue' import { createPinia } from 'pinia' import App from './App.vue'
const pinia = createPinia() const app = createApp(App)
app.use(pinia) app.mount('#app')
|
使用的最佳实践,是将它定义在一个单独文件中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', { state: () => ( return { name:"defaultName", count: 0 } ), getters: { getCount(): number { return this.count; }, getName(): string { return this.name; }, double: (state) => state.count * 2, greeting(str: string) { return str + this.name; }, }, actions: { increment() { this.count++ }, setName(name: string) { this.name = name; }, setCount(n: number) { this.count = 500 + n; }, }, })
|
1. state
state 被定义为一个返回初始状态的函数,可以在 vue 文件中引入后进行查询、修改、重置其值。
修改值有 5 种方法:
- 直接修改
- 在
$patch
中通过属性修改
- 推荐:在
$patch
中通过工厂函数修改
$state
中通过属性修改,但必须修改整个对象
- 通过 Pinia 中
actions
修改
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| import { useCounterStore } from './store'
const Counter = useCounterStore()
const change1 = () => { Counter.count++ }
const change2 = () => { Counter.$patch({ count:200, name:"hello" }) }
const change3 = () => { Counter.$patch((state)=>{ state.count = 300, state.name = "world" }) }
const change4 = () => { Counter.$state = ({ count: 400, name: "hello world" }) }
const change5 = () => { Counter.setCount(100) }
|
2. getters
getters
完全等同于 store 的 state
的计算值,使用 this
访问整个 store 实例。
推荐使用箭头函数,并且它将接收 state 作为第一个参数。
1 2 3 4 5 6
| getters: { otherGetter(state) { const otherStore = useOtherStore() return state.localData + otherStore.data }, },
|
可以像访问 state
属性一样,直接访问任何 getter
1 2 3 4
| const store = useCounterStore() store.count = 3 store.doubleCount
|
3. actions
action
相当于组件中的 method
,也使用 this
访问整个 store 实例,并且它们也是定义业务逻辑的完美选择。
在其中,可以使用同步和异步方法,也可以互相调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| type User = { name: string; age: number; };
const Login = (): Promise<User> => { return new Promise((resolve) => { setTimeout(() => { resolve({ name: "小明", age: 18, }); }, 2000); }); };
export const useUserStore = defineStore(Names.User, { state: () => { return { user: <User>{}, name: "初始值" }; }, actions: { async setUser() { const result = await Login(); this.user = result; this.setName("异步调用同步方法"); }, setName(name: string) { this.name = name; }, }, getters: {} });
|
4. 其他 api
$reset
: 重置为初始值
$subscribe
: state
中值变化时,会被调用
$onAction
: actions
调用时,会被调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| const User = useUserStore()
const reset = () => { User.$reset() } User.$subscribe((mutation, state) => { console.log("mutation=",mutation); console.log("state=",state); })
User.$onAction(({ name, // action 名称 store, // store 实例,类似 `someStore` args, // 传递给 action 的参数数组 after, // 在 action 返回或解决后的钩子 onError, // action 抛出或拒绝的钩子 }) => { console.log("onAction name=",name); console.log("onAction store=",store); console.log("onAction args=",args); console.log("onAction after=",after); console.log("onAction onError=",onError); })
|
3. 注意
1. 解构
解构后的属性值,默认不具有响应式,可以使用 pinia 提供的 api 将它转换为响应式的
1 2 3 4 5 6 7 8 9
| import { useCounterStore } from './store' import { storeToRefs } from 'pinia';
const Counter = useCounterStore() const User = useUserStore()
const {count, name} = Counter
const {name, age} = storeToRefs(User)
|