Vue 基础-Pinia(第六章)

1. Pinia

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
// main.ts
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
// index.tx
import { defineStore } from 'pinia'

// 你可以对 `defineStore()` 的返回值进行任意命名,
// 推荐使用 store 的名字,同时以 `use` 开头且以 `Store` 结尾,
// 比如 `useUserStore`,`useCartStore`,`useProductStore` 等
// 第一个参数是你的应用中 Store 的唯一 ID。
// 第二个参数是 Setup 函数或 Option 对象。
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
// xxx.vue
import { useCounterStore } from './store'

const Counter = useCounterStore()

// 1: 直接修改
const change1 = () => {
Counter.count++
}
// 2: $patch 中通过属性修改
const change2 = () => {
Counter.$patch({
count:200,
name:"hello"
})
}
// 3:(推荐)$patch 中通过工厂函数修改
const change3 = () => {
Counter.$patch((state)=>{
state.count = 300,
state.name = "world"
})
}
// 4: $state 中通过属性修改,但必须修改整个对象
const change4 = () => {
Counter.$state = ({
count: 400,
name: "hello world"
})
}
// 5: 通过 pinia 中 actions 修改
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
// xxx.vue
const store = useCounterStore()
store.count = 3
store.doubleCount // 6

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
// index.ts
type User = {
name: string;
age: number;
};

const Login = (): Promise<User> => {
return new Promise((resolve) => {
// 模拟异步请求,延迟 2s
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
// xxx.vue
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)