介绍
Vue3使用Pinia全局状态管理
# 什么是Pinia
Pinia是Vue生态里Vuex的代替者,一个全新Vue的状态管理库。在Vue3成为正式版以后,尤雨溪强势推荐的项目就是Pinia。那先来看看Pinia比Vuex好的地方,也就是Pinia的五大优势。
- 可以对Vue2和Vue3做到很好的支持,也就是老项目也可以使用Pinia。
- 抛弃了Mutations的操作,只有state、getters和actions.极大的简化了状态管理库的使用,让代码编写更加容易直观。
- 不需要嵌套模块,符合Vue3的Composition api ,让代码更加扁平化。
- 完整的TypeScript支持。Vue3版本的一大优势就是对TypeScript的支持,所以Pinia也做到了完整的支持。如果你对Vuex很熟悉的化,一定知道Vuex对TS的语法支持不是完整的(经常被吐槽)。
- 代码更加简洁,可以实现很好的代码自动分割。Vue2的时代,写代码需要来回翻滚屏幕屏幕找变量,非常的麻烦,Vue3的
Composition api
完美了解决这个问题。 可以实现代码自动分割,pinia也同样继承了这个优点。
# 创建Pinia 仓库
- 通过create-vue (opens new window) 创建一个Vue3项目 并勾选Pinia 就可以直接使用
# 创建一个state
- 和Vuex类似 Pinia拥有:
id
仓库的名称 作为唯一标识state
声明状态储存数据 (相当于data储存数据)getters
计算状态变化 (相当于watch监听数据变化)actions
进行一些方法逻辑处理 (相当于methods方法)
- 与Vuex比 Pinia取消了繁琐的 同步异步逻辑处理 所以的逻辑性方法只需要
actions
统一处理即可
创建一个Pinia仓库并使用
- 和Vuex一样 在
src
下 创建一个stores
文件 作为仓库文件夹
- 创建文件后 需要引入Pinia仓库的方法 进行创建
// 1. 定义状态容器和数据
// 2. 定义容器中的state (全局数据状态/储存)
// 3. 仓库中的action的使用
// 引入pinia仓库(容器)
import { defineStore } from 'pinia'
export const useCounterStore = defineStore({
// 给仓库起一个名称(唯一标识)
id: 'counter',
// 声明状态储存数据 (相当于data储存数据)
state: () => ({
counter: 666
}),
// 计算状态变化 (相当于watch监听数据变化)
getters: {},
// 进行一些方法逻辑处理 (相当于methods方法)
actions: {}
})
- 定义
state
也可以通过return
设置数据- 📕官方推荐使用箭头函数(
()=>{}
)获得更好的类型推断
- 📕官方推荐使用箭头函数(
export const useCounterStore = defineStore({
id: 'counter',
state: () => {
// 通过return的方式定义state
return {
counter: 123
}
},
getters: {
doubleCount: (state) => state.counter * 2
},
actions: {
increment() {
this.counter++
}
}
- 在Vue文件中 进行导入Pinia仓库 就可以使用啦
<template>
<main>{{ store.counter }}</main>
</template>
<script lang="ts" setup>
// 导入pinia
import { useCounterStore } from '@/stores/counter'
// 实例化pinia
const store = useCounterStore()
console.log(store)
</script>
- 想在
template
解构使用store
中的值 可以通过官方提供的storeToRefs (opens new window) 进行解构(类似于Vue3中的toRefs()
- 不要直接解构 直接解构会造成数据失去响应式
<template>
<main>{{ counter }}</main>
</template>
<script lang="ts" setup>
// 导入pinia的storeToRefs解构方法
import { storeToRefs } from 'pinia'
// 导入全局仓库
import { useCounterStore } from '@/stores/counter'
// 实例化pinia
const store = useCounterStore()
// 解构数据
const { counter } = storeToRefs(store)
</script>
- 使用Vue3的toRefs() (opens new window) 也可以进行解构操作 但是不知道兼容性怎样 建议还是使用Pinia提供的
storeToRefs
吧
<template>
<main>{{ counter }}</main>
</template>
<script lang="ts" setup>
// 使用Vue3的toRefs解构数据
import { toRefs } from 'vue'
// 导入全局仓库
import { useCounterStore } from '@/stores/counter'
// 实例化pinia
const store = useCounterStore()
// 解构数据
const { counter } = toRefs(store)
</script>
- 其实在
Vuex
中,直接解构数据也是不可以的。
# 修改状态数据(state)方式
- 修改
state
总共有四种方式- 前两种 适合简单无逻辑较少的修改
- 第三种 适合逻辑性多数据的修改
- 第四种 是写在
actions
也就是Pinia中 适合全局复用性的逻辑修改
# 方式1 直接声明方法修改
- 可以在Vue中直接修改
state
的值 这种方法最简单 但是只适合修改单一数据- 适合简单无逻辑较少的修改
<template>
<main>{{ counter }}</main>
<div>
<button @click="changeCounter">修改内容</button>
</div>
</template>
<script lang="ts" setup>
import { storeToRefs } from 'pinia'
import { useCounterStore } from '@/stores/counter'
//导入Pinia
const store = useCounterStore()
// 修改Pinia的全局状态
const changeCounter = () => {
//修改Pinia的值
store.counter++
}
// 解构数据
const { counter } = storeToRefs(store)
</script>
# 方式2 $patch修改多条数据
store.$patch({})
可以修改多条数据 这种修改方式 有点类似于微信小程序this.setData()
方法 可以进行批量无逻辑性的修改- 适合简单无逻辑较少的修改
<template>
<main>
{{ counter }}
{{ message }}
</main>
<div>
<button @click="changeCounter">修改内容</button>
</div>
</template>
<script lang="ts" setup>
import { storeToRefs } from 'pinia'
import { useCounterStore } from '@/stores/counter'
//导入Pinia
const store = useCounterStore()
// 修改Pinia的全局状态
const changeCounter = () => {
// 通过$patch 进行修改
store.$patch({
counter: store.counter + 2,
message: 'hello'
})
}
// 解构数据
const { counter, message } = storeToRefs(store)
</script>
# 方式3 $patch进行函数的形式修改数据
store.$patch((state) => {})
这时候的state就是store
仓库里的state
,所以我们可以直接在函数里改变任何状态数据的值。- 适合逻辑性多数据的修改
<template>
<main>
{{ counter }}
{{ message }}
</main>
<div>
<button @click="changeCounter">修改内容</button>
</div>
</template>
<script lang="ts" setup>
import { storeToRefs } from 'pinia'
import { useCounterStore } from '@/stores/counter'
//导入Pinia
const store = useCounterStore()
// 修改Pinia的全局状态
const changeCounter = () => {
// 通过$patch函数的方式进行修改
store.$patch((state) => {
state.counter++
state.message = 'hello'
})
}
// 解构数据
const { counter, message } = storeToRefs(store)
</script>
# 方式4 在actions中声明方法 再调用actions
- 可以先在
store
里,定义好actions
中的函数,然后在组件里再调用函数。 - 在用
actions
的时候,不能使用箭头函数,因为箭头函数绑定是外部的this
。 - 在Pinia内部中 可以用this来调用
state
的值 有点类似于class
中的this
- 适合全局复用性的逻辑修改
先在Pinia中定义actions方法
// 引入pinia仓库(容器)
import { defineStore } from 'pinia'
export const useCounterStore = defineStore({
id: 'counter',
state: () => {
return {
counter: 123,
message: '你好'
}
},
getters: {},
// 进行一些方法逻辑处理 (相当于methods方法)
actions: {
// 在Pinia中处理逻辑
changeStore() {
this.counter++
this.message = 'hello'
}
}
在Vue中使用
<template>
<main>
{{ counter }}
{{ message }}
</main>
<div>
<button @click="changeCounter">修改内容</button>
</div>
</template>
<script lang="ts" setup>
import { storeToRefs } from 'pinia'
import { useCounterStore } from '@/stores/counter'
//导入Pinia
const store = useCounterStore()
// 声明一个方法
const changeCounter = () => {
// 直接调用Pinia中的actions声明的方法
store.changeStore()
}
// 解构数据
const { counter, message } = storeToRefs(store)
</script>
# Pinia中的getters
- Pinia中的
getters
和Vue中的计算属性几乎一样,就是在获取state
的值时作一些处理。 - 在Vue中 直接调用
getters
方法 对state
数据进行处理
先在Pinia中定义getters方法
// 引入pinia仓库(容器)
import { defineStore } from 'pinia'
export const useCounterStore = defineStore({
id: 'counter',
state: () => {
return {
message: '大家好啊'
}
},
// 计算状态变化 (相当于watch监听数据变化)
getters: {
// 处理state的值
changeMessage(state) {
return (state.message = state.message + '我是小刘')
}
},
actions: {}
})
在Vue中使用
<template>
<main>{{ changeMessage }}</main>
</template>
<script lang="ts" setup>
import { storeToRefs } from 'pinia'
import { useCounterStore } from '@/stores/counter'
//导入Pinia
const store = useCounterStore()
// 解构数据 使用处理后的state数据
const { changeMessage } = storeToRefs(store)
</script>
# getters的缓存特性
getters
是有缓存特性的 也就是说 如果state
的值和原来一样 那么将不会进行调用- 比如在模板中多次用到同样的
getters
方法 且state
不变 那么getters
只会调用一次
- 比如在模板中多次用到同样的
getters
的数据是响应式数据 如果对应的state
值改变 那么getters
也会随着调用一次,清除以前的数据缓存
<template>
<!-- getters具备响应式 数据修改后 会更新 -->
<main>{{ changeMessage }}</main>
<div>
<button @click="changeState">修改state</button>
</div>
</template>
<script lang="ts" setup>
import { storeToRefs } from 'pinia'
import { useCounterStore } from '@/stores/counter'
//导入Pinia
const store = useCounterStore()
//修改state的值
const changeState = () => {
store.message = 'hello'
}
// 解构数据 使用处理后的state数据
const { changeMessage } = storeToRefs(store)
</script>
# 关于Pinia的this使用
actions
和getters
都可以使用this
因为他指向的是当前文件Pinia的全局 不过如果你用TS当你getters
不穿参 就会出现TS报错- 因为我们使用的时TS,所以如果我们不传
state
, TypeScript是无法自动推到出来返回的数据类型的,所以这里我们要标明返回类型 就不会提示错误了。
- 因为我们使用的时TS,所以如果我们不传
getters: {
// 处理state的值
changeMessage(): string {
return (this.message = this.message + '我是小刘')
}
},
# 多个文件Store调用
- 文件A想用文件B的
state
那么设计到多个Store
仓库的使用 通常我们只需要import
引入使用即可- 注意: 每个
Store
仓库的ID
要是唯一的。并且引入的Store
仓库是一个函数方法不是属性!
- 注意: 每个
定义一个仓库B
import { defineStore } from 'pinia'
export const userInfo = defineStore({
// 仓库id是唯一的
id: 'userInfo',
state: () => {
return {
myNmae: '小刘'
}
}
})
在仓库A中引用仓库B
- 注意: 每个
Store
仓库的ID
要是唯一的。并且引入的Store
仓库是一个函数方法不是属性!
import { defineStore } from 'pinia'
// 引入仓库B
import { userInfo } from './user'
export const useCounterStore = defineStore({
// 仓库id是唯一的
id: 'counter',
state: () => {
return {
message: '我的名字叫'
}
},
getters: {
// 数据调用的时候进行处理
changeMessage(): string {
// 使用仓库B的内容
return this.message + userInfo().myNmae // 引入的其他Store是一个函数方法
}
},
actions: {}
})
# 在调试工具中查看Pinia
- 如果我们安装了Vue浏览器插件 我们直接打开查看即可
- 这时候在上半部分有一个面板,默认显示是
Components
,点击后,也可以选择Pinia
。这时候点击Pinia
就可以看到store里边的state、getters...
等信息了。