pinia与 Vuex 的比较
Pinia 最初是为了探索 Vuex 的下一次迭代会是什么样子,结合了 Vuex 5 核心团队讨论中的许多想法。最终,我们意识到 Pinia 已经实现了我们在 Vuex 5 中想要的大部分内容,并决定实现它 取而代之的是新的建议。
与 Vuex 相比,Pinia 提供了一个更简单的 API,具有更少的规范,提供了 Composition-API 风格的 API,最重要的是,在与 TypeScript 一起使用时具有可靠的类型推断支持。
核心概念:
pinia从使用角度和之前的Vuex几乎是一样的。
Store是一个保存状态和业务逻辑的实体,它不绑定到您的组件树。换句话说,它承载全局state。它有点像一个始终存在的组件,每个人都可以读取和写入。它有三个核心概念。
- State
- Getters
Mutations- Actions(同步异步都支持)
vuex有四个核心概念(State,Getters,Mutations,Actions)
与vuex中一样,简单介绍一下这三个核心概念。
state:类似组件的data,用来存储全局状态。
getters:类似组件的computed,根据已有State封装派生数据,也具有缓存的特性
actions:类似组件的methods,用来封装业务逻辑,同步异步都可以
pinia使用示例
代码实现 创建一个store:
import { defineStore } from 'pinia'
// 1.定义并导出容器
// 参数1:容器的 ID ,必须唯一,将来Pinia会把所有的容器挂载到根容器
// 参数2:选项对象
// 返回值:一个函数,调用得到容器实例
export const useMainStore = defineStore('main',{
/**
* 类似于组件的data,用来存储全局状态的
* 1.必须是函数:这样是为了在服务器端渲染的适合避免交叉请求导致的数据状态污染
* 2.必须是箭头函数,这是为了更好的js类型推导
*/
state:() => {
return {
count: 100,
foo: 100
}
},
/**
* 类似于组件的computed,用来封装计算数学,有缓存功能
*/
getters:{},
/**
* 类似于组件的methods,封装业务逻辑,修改state
*/
actions:{}
})
组件中使用
<template>
<div>
<p>{
{ mainStore.count }}</p>
<p>{
{ mainStore.foo }}</p>
<hr>
<p>{
{ count }}</p>
<p>{
{ foo }}</p>
<hr>
<button @click='handleChangeState'>修改数据</button>
</div>
</template>
<script setup>
import { useMainStore } from '../store';
import { storeToRefs} from 'pinia'
const mainStore = useMainStore()
console.log(mainStore.count)//打印在控制台测试调用
//这是有问题的,因为这样拿到的数据不是响应式的,是一次性的
//Pinia其实就是把state数据都做了 reactive处理了
const {count , foo } = mainStore
//正确方法
const {count , foo } = storeToRefs(mainStore)
//修改数据函数
const handleChangeState = () => {
mainStore.count++
}
</script>
使用时不能直接解构得到store中的变量,因为这样得到的变量是非响应式的。
解决方式:使用官方的storeToRef
修改方式(注意看handleChangeState方法内)
state中增加一个变量:数组arr:[1,2,3]
<template>
<div>
<p>{
{ mainStore.count }}</p>
<p>{
{ mainStore.foo }}</p>
<hr>
<p>{
{ count }}</p>
<p>{
{ foo }}</p>
<hr>
<button @click='handleChangeState'>修改数据</button>
</div>
</template>
<script setup>
import { useMainStore } from '../store';
import { storeToRefs} from 'pinia'
const mainStore = useMainStore()
console.log(mainStore.count)//打印在控制台测试调用
//这是有问题的,因为这样拿到的数据不是响应式的,是一次性的
//Pinia其实就是把state数据都做了 reactive处理了
//const {count , foo } = mainStore
//正确方法
const {count , foo } = storeToRefs(mainStore)
//修改数据函数
const handleChangeState = () => {
//方式1:最简单的方式就是这样
mainStore.count++
//方式2:如果需要修改多个数据,建议使用 $patch 批量更新(内部做了性能优化)
mainStore.&patch({
count: mainStore.count +1,
foo: 'hello'
arr: [...mainStore.arr,4]
})
//方式3:$patch 一个函数
mainStore.&patch(state => {
state.count++
state.foo = 'hello'
state.arr.push(4)
})
//方式4:逻辑比较多的适合可以封装到 actions 做处理
}
</script>
最后讲一下方式4:
回到store.js中
import { defineStore } from 'pinia'
// 1.定义并导出容器
// 参数1:容器的 ID ,必须唯一,将来Pinia会把所有的容器挂载到根容器
// 参数2:选项对象
// 返回值:一个函数,调用得到容器实例
export const useMainStore = defineStore('main',{
/**
* 类似于组件的data,用来存储全局状态的
* 1.必须是函数:这样是为了在服务器端渲染的适合避免交叉请求导致的数据状态污染
* 2.必须是箭头函数,这是为了更好的js类型推导
*/
state:() => {
return {
count: 100,
foo: 100,
arr: [1,2,3]
}
},
/**
* 类似于组件的computed,用来封装计算数学,有缓存功能
*/
getters:{},
/**
* 类似于组件的methods,封装业务逻辑,修改state
*/
actions:{
changeState() {
this.count++;
this.foo = 'hello'
this.arr.push(4)
}
}
})
可以看到我们在actions中封装了一个方法
然后可以在组件中通过store的实例调用该方法
回到组件(注意看方式4)
<template>
<div>
<p>{
{ mainStore.count }}</p>
<p>{
{ mainStore.foo }}</p>
<p>{
{ mainStore.arr }}</p>
<hr>
<p>{
{ count }}</p>
<p>{
{ foo }}</p>
<hr>
<button @click='handleChangeState'>修改数据</button>
</div>
</template>
<script setup>
import { useMainStore } from '../store';
import { storeToRefs} from 'pinia'
const mainStore = useMainStore()
const {count , foo } = storeToRefs(mainStore)
//修改数据函数
const handleChangeState = () => {
//方式1:最简单的方式就是这样 ...
//方式2:如果需要修改多个数据,建议使用 $patch 批量更新(内部做了性能优化) ...
//方式3:$patch 一个函数 ...
//方式4:逻辑比较多的适合可以封装到 actions 做处理
mainStore.changeState()
}
</script>
当然,该方法也可以进行设置参数传参( 参数名 :参数类型 , .....)
比如,count的增加我们不指定加多少,而是根据传入的参数来决定增加的幅度。
那么changeStore方法将改为
...
actions:{
//注意:不能使用箭头函数定义action,因为箭头函数绑定外部this
changeState(num: number){
this.count+=num
this.foo = 'hello'
this.arr.push(4)
}
}
...
接下来讲解Getter的使用
回到store.js
import { defineStore } from 'pinia'
export const useMainStore = defineStore('main',{
state:() => {
return {
count: 100,
foo: 100,
arr: [1,2,3]
}
},
/**
* 类似于组件的computed,用来封装计算数学,有缓存功能
*/
getters:{
//函数接收一个可选参数: state 状态对象
count10(state){
return state.count + 10
}
},
actions:{
...
}
})
注意,如果在getter之中 使用了this,则必须手动指定返回值的类型,否则类型推导不出来
参考资料:官方文档:Pinia 中文文档 (web3doc.top)
b站学习视频: Pinia_哔哩哔哩_bilibili