Vue3状态管理工具Pinia的使用

Vue3全面拥抱TypeScript,而其自家的状态管理工具Vuex对于TypeScript的支持并不友好,所以在Vue3时代Pinia显然是更好的选择。

Pinia的开发者是Vue的核心开发人员,且Pinia已经被纳入Vue的官方库,其作者也表示将来可能会与Vuex合并,所以学习Pinia并不需要担心他是否适合Vue,他一定是稳定可用的。

简单介绍

核心概念

Pinia从使用角度和之前的Vuex基本是一致的。只是不再需要mutations了

Store是一个保存状态和业务逻辑的实体,不会绑定到组件树,它承载全局的state。它有三个核心概念:

  • state:存储全局状态,类似组件的data
  • getters:根据已有state封装派生数据,也具有缓存的特性,类似组件的computed
  • actions:用于封装业务逻辑,同步和异步都可以,类似组件的methods,也就不需要mutations了

安装

1
2
3
yarn add pinia
// or
npm install pinia

初始化配置

1
2
3
4
5
6
// main.ts
import { createApp } from 'vue'
import App from './App.vue'
import { createPinia } from 'pinia'
const pinia = createPinia()
createApp(App).use(pinia).mount('#app')

语法

基本使用

我们定义的所有store通过id进行区分,所以id必须是唯一的。

id的传入方式有两种:

  • defineStore( id, { …. })
  • defineStore({ id: ‘’, …. })
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
// store/index.ts
import { defineStore } from "pinia";

export const useMainStore = defineStore( 'main', {
/**
* 类似于组件的data,用来存储全局状态
* 1. 必须是函数且必须是箭头函数
*/
state: () => {
return {
count: 100
}
},
// state也可以这么简写
// state: () => ({
// count: 100
// })
/**
* 用来封装计算属性,有缓存功能
*/
getters: {},
/**
* 封装业务逻辑,修改state
*/
actions: {}
})

在组件中使用:

1
2
3
4
import { useMainStore } from '../store';
// 得到容器实例
const mainStore = useMainStore()
console.log(mainStore.count)

解构数据

我们知道在vue3中如果需要解构数据且不丢失响应式需要用到toRefs(),在Pinia中我们也需要使用它提供的storeToRefs()来实现状态数据的解构且保持响应式。

1
2
3
4
5
6
7
8
9
10
// store/index.ts
import { defineStore } from "pinia";
export const useMainStore = defineStore( 'main', {
state: () => {
return {
count: 100,
name: 'ali'
}
},
})
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
<!-- 组件 -->
<template>
<p>{{mainStore.count}}</p>
<p>{{mainStore.name}}</p>
<hr>
<p>{{count}}</p>
<p>{{name}}</p>
<hr>
<p>
<button @click="handleChangState">
修改数据
</button>
</p>
</template>
<script lang="ts" setup>
import { storeToRefs } from 'pinia';
import { useMainStore } from '../store';
// 得到容器实例
const mainStore = useMainStore()

// 使用官方API进行解构,类似于Vue3中提供的toRefs
const { count, name } = storeToRefs(mainStore)
// 解构出来的数据是ref代理的,所以在js中使用需要加.value
console.log(count.value)

// 修改state中的数据,解构后的数据也能监听到变化
const handleChangState = () => {
mainStore.count += 1
}
</script>

状态更新

修改state中的数据有多种方式:

// 修改数据方式一: 直接修改

1
2
mainStore.count += 1
mainStore.name += 'ss'

// 修改数据方式二: 修改多个数据建议使用$patch批量更新

1
2
3
4
mainStore.$patch({
count: mainStore.count + 2,
name: mainStore.name + '--'
})

// 修改数据方式三: $patch一个函数,函数接受一个参数就是state对象,更好的批量更新方式,常用于修改引用类型数据,使用$patch的时候更推荐这种方式

1
2
3
4
5
mainStore.$patch( state => {
state.count ++
state.name += '--'
state.arr.push(4)
})

// 修改数据方式四: 通过action来修改数据,逻辑比较多的时候就可以封装到action中来操作,action内部也可以使用$patch来批量更新数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// store/index.ts
actions: {
// 一定不能使用箭头函数来定义action,因为箭头函数会绑定外部this,函数内部this就拿不到数据了
// action默认不接受参数,可以接受自定义参数且可以接受多个
changeState( num: number, str: string ) {
// actions中通过this来访问state中的数据
// this.count += 3
// this.name += '--'
// this.arr.push(4)
// actions中一次修改多个数据也推荐使用$patch
this.$patch( state => {
state.count += num
state.name += str
state.arr.push(num)
})
}
}
1
2
// 组件
mainStore.changeState(10, 'ss')

需要注意的一点是:actions中的函数与vue2中的methods类似,一定不能定义成箭头函数,不然会导致函数内部的this指向出问题,拿不到state中的数据

Pinia还提供了$reset方法将容器上的数据重置为初始状态: mainStore.$reset()

getters

getters默认接受一个可选参数:state状态对象

  • 如果传递了state可以帮助ts进行类型推导
  • 如果不传递state使用this来访问,则需要手动指定返回值的类型

如果在getter中需要访问其他getter,就可以使用this的方式来使用,不过注意的是,使用this的话那么getter不能被定义为箭头函数

getter也具有缓存功能,依赖数据不发生变化,不会多次调用

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
// store/index.ts
import { defineStore } from "pinia";
export const useMainStore = defineStore( 'main', {
state: () => {
return {
count: 100,
name: 'ali',
arr: [1, 2, 3]
}
},
getters: {
// getters默认接收一个可选参数:state状态对象
// 使用state对象可以让ts进行类型推导
count10 (state) {
console.log('getters调用了')
return state.count + 10
}
// 也可以不传递参数,使用this来访问state
// 但是这样会导致ts没办法推导出返回值类型,需要手动指定
// 如果用this来访问,就不能使用箭头函数
// count10 () :number {
// console.log('getters调用了')
// return this.count + 10
// }
},


})

组件外部使用

组件外部使用Pinia需要手动传入store实例才可以

作者

胡兆磊

发布于

2022-02-25

更新于

2022-10-23

许可协议