官网地址: Pinia 🍍
前言
最近完全使用Vue3的技术框架进行业务开发了,在Vue3
的体系下,一般用Pinia
来代替Vuex
来作为统一状态管理。趁着这个机会,总结一波Pinia
的使用。
Store
(如 Pinia
) 是一个保存状态和业务逻辑的实体,它并不与你的组件树绑定。换句话说,它承载着全局状态。它有点像一个永远存在的组件,每个组件都可以读取和写入它。它有三个概念,state
、getter
和 action
,我们可以假设这些概念相当于组件中的 data
、 computed
和 methods
。
优点
就我自己使用的感受来说,
- 比之前
Vuex
使用起来更方便,约束没有那么多,也不再区分mutation
和action
- 模块化定义,可以定义多个
store
,使用起来就和平常模块一样 store
里面还可以引用另外store
里面的state
,getter
和action
,非常方便- 在
store
的action
里面,可以直接调用异步方法
安装&配置
如果是通过Vue的脚手架新建项目,可以直接选择Pinia,不需要单独安装。
# 通过npm包进行安装
npm install pinia
// main.js
import { createApp } from 'vue'
// 引入pinia
import { createPinia } from 'pinia'
import App from './App.vue'
// 创建实例
const pinia = createPinia()
const app = createApp(App)
// use
app.use(pinia)
app.mount('#app')
使用
定义和使用Store
建议可以在src文件夹下新建一个store
文件夹,专门放置各种store
。像在优点中介绍的,Pinia
使用模块化设计,首先需要定义一个Store
来管理状态。
// @/stores/counter.js
import { defineStore } from 'pinia'
// 第一个参数是你的应用中 Store 的唯一 ID。
export const useCounterStore = defineStore('counter', {
// 其他配置...
state: () => {
return {
count: 0,
}
},
getters: {},
actions: {}
})
使用defineStore
来定义,defineStore
接受两个参数,第一个参数是应用中Store
的唯一ID,第二个参数里面配置state
,getters
,actions
。
👆一般约定使用
use
开头,以Store
结尾来命名defineStore
的返回值。像上面代码中的useCounterStore
。
定义好了的Store
直接在需要使用的地方引入使用就行,不需要像Vuex
之前Module
一样声明。
<script setup>
import { useCounterStore } from '@/stores/counter'
// 可以在组件中的任意位置访问 `store` 变量
const store = useCounterStore()
</script>
定义和使用State
State
可以类比做Vue2
里面的响应式数据data
,是Store
里面管理的全局状态。定义state非常简单,就像以前声明data
数据是一样的。
export const useCounterStore = defineStore('counter', {
// 在这里定义state,state是个函数,需要返回一个对象
state: () => {
return {
count: 0,
items: [],
hasChanged: true,
}
}
})
使用的话直接引入Store
后,可以直接使用和赋值。不需要再通过提交mutation
来改变状态的值。
const counterStore = useCounterStore()
// 直接赋值
counterStore.count++
// 也可以批量更新
counterStore.$patch({
count: store.count + 1,
hasChanged: false,
})
✌️上述两种方式都会触发响应式更新。Store里面的state和使用reactive进行响应式包装一样,可以直接在template中使用counterStore.count,也可以直接在script中counterStore.count操作,不需要counterStore.count.value。
定义和使用getter
getter
可以类比Vue2
里面的computed
,它和computed
一样会自动计算里面使用到的依赖,当依赖发生更新时自动更新。
import { useOtherStore } from './other-store'
export const useCounterStore = defineStore('counter', {
state: () => ({
count: 0,
}),
getters: {
// 推荐使用箭头函数,并且它将接收 state 作为第一个参数
doubleCount: (state) => state.count * 2,
// 也可以使用普通函数,普通函数内可以使用this访问整个state和getter
doublePlusOne() {
return this.doubleCount + 1
},
// 还可以使用其它Store里面的state和getter,并且也是响应式的
// 这就非常方便
otherGetter(state) {
const otherStore = useOtherStore()
return state.count + otherStore.data
},
},
})
在Vue
组件里面使用getter
就和state
一样,也是被当做属性使用,这里不再赘述。
解构State和Getter
请注意,**store **
**是一个用 ****reactive **
包装的对象,这意味着不需要在 getters
后面写 .value
。就像 setup
中的 props
一样,我们不能对它进行直接解构:
<script setup>
import { useCounterStore } from '@/stores/counter'
import { storeToRefs } from 'pinia'
const store = useCounterStore()
// ❌ 这将不起作用,因为它破坏了响应性
// 这就和直接解构 `props` 一样
const { name, doubleCount } = store
name // 将始终是 "Eduardo"
doubleCount // 将始终是 0
setTimeout(() => {
store.increment()
}, 1000)
// ✅ 这样写是响应式的
// 这样解构之后,可以当做当个的ref使用
const { count, doubleCount } = storeToRefs(store)
// action的话可以直接解构
const { increment } = store
</script>
定义和使用action
action
可以类比Vue2
里面的method
,通过this访问整个store
实例,并且支持异步。
import { useOtherStore } from './other-store'
export const useCounterStore = defineStore('counter', {
state: () => ({
count: 0,
}),
getters: {
// 推荐使用箭头函数,并且它将接收 state 作为第一个参数
doubleCount: (state) => state.count * 2,
// 也可以使用普通函数,普通函数内可以使用this访问整个state和getter
doublePlusOne() {
return this.doubleCount + 1
},
// 还可以使用其它Store里面的state和getter,并且也是响应式的
// 这就非常方便
otherGetter(state) {
const otherStore = useOtherStore()
return state.count + otherStore.data
},
},
actions: {
// 类似 getter,action 也可通过 this 访问整个 store 实例
increment() {
this.count++
},
// 并且是可以异步的
// 你也完全可以自由地设置任何你想要的参数以及返回任何结果
// 在这里也可以访问其它Store里面的action
asyncIncrement() {
try {
this.count = await api.post({ data })
} catch (error) {
return error
}
},
},
})
结尾
整个Pinia
还支持ts
类型的自动推断,支持Vue3
的setup
语法,这部分内容可以自行查询 官网 Pinia 🍍。
在第一次开发过程中,一直感叹这个Store
太好用了,还可以用来封装一个模块的业务逻辑。
但是使用Vue3
的hooks
也是可以做到的,那什么时候用Store
,什么时候用hooks
呢?
我认为,当你这个状态全局多个组件会用到,并且是使用同一份状态的基础下,可以使用Store
。如果你只是想封装一段业务逻辑,在不同组件里面复用,这个时候使用hooks
。
以上仅是个人学习过程中的一点总结,如有错误,敬请指正。