~~我正在参加「掘金·启航计划」~~
ps:以下代码环境均基于vue3,具体信息可参考pinia中文官网
1. pinia安装
npm i pinia
2. 引入pinia
在main.js或main.ts中将pinia注册到vue实例
import { createApp } from 'vue'
// 导入创建pinia实例的方法 createPinia
import { createPinia } from 'pinia'
import App from './App.vue'
const app = createApp(App)
// 将创建的实例注册到vue实例中
app.use(createPinia())
app.mount('#app')
3. 创建pinia模块
每个pinia模块由一个唯一标识表示,包含三个概念:state、getters、actions。其中state相当于组件的“数据“,getters相当于组件中的“计算属性”,actions相当于组件中的”方法“。
一般会在src目录下新建一个store文件夹,来存放定义的pinia模块,如下所示,定义了一个counter.js文件,即counter模块,
import { defineStore } from 'pinia'
// 第一个参数是模块名称,唯一标识该模块,第二个参数为一个包含state、getters、actions属性的对象
export const useStore = defineStore('counter', {
state() { // 定义状态属性的方法,返回值为定义的状态属性对象
return {
count: 0,
count1: 10
}
},
getters: { // 相当于组件中的计算属性,可以缓存数据
},
actions: { // 相当于组件中的方法,多用于修改状态属性值
}
})
4. pinia的使用
4.1 vue模块中的简单使用
<template>
<div>
<button>{{store.count}}</button>
<h2>{{store.count1}}</h2>
</div>
</template>
<script setup>
// 导入我们counter模块定义的 store变量,该变量是一个方法,调用后返回一个能直接访问状态属性和定义的方法的对象
import { useStore } from '@/stores/counter'
const store = useStore()
</script>
注意:store 是一个用reactive 包裹的对象,这意味着不需要在getter 之后写.value,但是,就像setup 中的props 一样,我们不能对其进行解构。
如果想要解构后直接访问,那么可以借助storeToRefs()方法,具体用法如下:
<template>
<div>
<button>{{count}}</button>
<h2>{{count1}}</h2>
</div>
</template>
<script setup>
// 导入我们counter模块定义的 store变量,该变量是一个方法,调用后返回一个能直接访问状态属性和定义的方法的对象
import { useStore } from '@/stores/counter'
import { storeToRefs } from 'pinia';
const store = useStore()
// 通过storeToRefs实现store对象的解构
const {count, count1} = {...storeToRefs(store)}
</script>
注意:storeToRefs只能解构响应式属性,跳过任何 action 或 非响应式(不是 ref/reactive)的属性
4.2 修改pinia状态属性的值
4.2.1 直接修改
<template>
<div>
<button @click="add">{{count}}</button>
<h2>{{count1}}</h2>
</div>
</template>
<script setup>
// 导入我们counter模块定义的 store变量,该变量是一个方法,调用后返回一个能直接访问状态属性和定义的方法的对象
import { useStore } from '@/stores/counter'
import { storeToRefs } from 'pinia';
const store = useStore()
// 通过storeToRefs实现store对象的解构
const {count, count1} = {...storeToRefs(store)}
const add = () => {
// 若使用的解构后的对象,则需要通过.value获取值
count.value++
// 直接通过store获取对象
store.count1++
}
</script>
4.2.2 通过$patch方法修改
1、传递对象修改
<template>
<div>
<button @click="add">{{count}}</button>
<h2>{{count1}}</h2>
</div>
</template>
<script setup>
// 导入我们counter模块定义的 store变量,该变量是一个方法,调用后返回一个能直接访问状态属性和定义的方法的对象
import { useStore } from '@/stores/counter'
import { storeToRefs } from 'pinia';
const store = useStore()
// 通过storeToRefs实现store对象的解构
const {count, count1} = {...storeToRefs(store)}
const add = () => {
store.$patch({
count: store.count + 1, // 注意:这里不能使用++,因为是把store.count+1的值赋给count,而不是它自身
count1: store.count1 + 10
})
}
</script>
2、传递函数修改
<template>
<div>
<button @click="add">{{count}}</button>
<h2>{{count1}}</h2>
</div>
</template>
<script setup>
// 导入我们counter模块定义的 store变量,该变量是一个方法,调用后返回一个能直接访问状态属性和定义的方法的对象
import { useStore } from '@/stores/counter'
import { storeToRefs } from 'pinia';
const store = useStore()
// 通过storeToRefs实现store对象的解构
const {count, count1} = {...storeToRefs(store)}
const add = () => {
store.$patch((state) => {
state.count++
state.count1 += 10
})
}
</script>
看到这里,可能觉得通过$patch修改的方式没有第一种方式简单,确实如此,不过$patch也有它的优势,不然存在的意义何在呢?是吧
用官方干的说法就是:针对$patch做了特殊优化的,怎么个特殊法,咋也不知道,希望有大神指点一二。
4.2.3 通过actions定义函数修改
对于复杂的业务逻辑,我们一般不在vue模板中修改状态属性值,而是在actions中定义方法来修改状态属性,这样方便维护。
import { defineStore } from 'pinia'
// 第一个参数是模块名称,唯一标识该模块,第二个参数为一个包含state、getters、actions属性的对象
export const useStore = defineStore('counter', {
state() { // 定义状态属性的方法,返回值为定义的状态属性对象
return {
count: 0,
count1: 10
}
},
getters: { // 相当于组件中的计算属性,可以缓存数据
},
actions: { // 相当于组件中的方法,多用于修改状态属性值
increment() {
this.count++
this.count1 += 10
}
}
})
<template>
<div>
<button @click="store.increment">{{count}}</button>
<h2>{{count1}}</h2>
</div>
</template>
<script setup>
// 导入我们counter模块定义的 store变量,该变量是一个方法,调用后返回一个能直接访问状态属性和定义的方法的对象
import { useStore } from '@/stores/counter'
import { storeToRefs } from 'pinia';
const store = useStore()
// 通过storeToRefs实现store对象的解构
const {count, count1} = {...storeToRefs(store)}
const add = () => {
store.$patch((state) => {
state.count++
state.count1 += 10
})
}
</script>
4.3 重置状态
执行调用store对象的$reset()方法即可,如下:
<template>
<div>
<button @click="store.increment">{{count}}</button>
<h2>{{count1}}</h2>
<button @click="store.$reset()">reset</button>
</div>
</template>
4.4 替换状态
修改store对象的$state属性即可,如下:
<template>
<div>
<button @click="store.increment">{{count}}</button>
<h2>{{count1}}</h2>
<button @click="store.$reset()">reset</button>
<br>
<button @click="store.$state = newState">newState</button>
</div>
</template>
<script setup>
// 导入我们counter模块定义的 store变量,该变量是一个方法,调用后返回一个能直接访问状态属性和定义的方法的对象
import { useStore } from '@/stores/counter'
import { storeToRefs } from 'pinia';
const store = useStore()
// 通过storeToRefs实现store对象的解构
const {count, count1} = {...storeToRefs(store)}
const add = () => {
store.$patch((state) => {
state.count++
state.count1 += 10
})
}
const newState = {
count: 99999,
count1: 100000
}
</script>
4.5 getters属性
Getter 完全等同于 Store 状态的计算属性,支持常规函数和箭头函数。
使用常规函数时可以通过 this 访问到 整个 store 的实例;使用箭头函数时,通过”状态“参数来获取属性。
// 常规函数方式
getters: { // 相当于组件中的计算属性,可以缓存数据
doubleCount() {
return this.count * 2 //通过this方法状态属性
},
doubleCountPlusOne() {
return this.doubleCount + 1 // 通过this访问前面定义的计算属性
}
}
// 箭头函数方式
getters: {
doubleCount: (state) => state.count * 2, // 通过state参数访问状态属性
doubleCountPlusOne: (state) => {
return state.doubleCount + 1 // 通过state参数访问前面定义的计算属性
}
}
<template>
<div>
<button @click="store.increment">{{count}}</button>
<h2>{{count1}}</h2>
<button @click="store.$reset()">reset</button>
<br>
<button @click="store.$state = newState">newState</button>
<h1>{{store.doubleCount}}</h1>
<h1>{{store.doubleCountPlusOne}}</h1>
</div>
</template>
4.6 state方法
state方法除了声明为常规函数外,也可以声明为箭头函数。箭头函数写起来更简单,如下:
state: () => ({
count: 0,
count1: 10
})
\