Vue3中使用状态管理库Pinia
前言
Pinia 的一些特点
- 同时支持 Vue2 和 Vue3
- 删除了
mutations
相关的操作,只保留state
getters
actions
,actions
中支持同步和异步 - Pinia中每个
store
都是独立的,不需要再嵌套模块,这样能让代码更简洁 - 支持
TypeScript
- 支持服务端渲染
本文记录了 Pinia
在 Vue3
中的使用过程,主要包括 state
getters
actions
三大模块的使用,了解Option Stores
和 Setup Stores
的使用差异。
初始化
先创建一个 Vue3 + TS
的应用,然后再安装 Pinia
。
使用 Vite 初始化一个 Vue3 的应用,执行命令:
pnpm create vite
输入项目名称,选择 TypeScript
版本,然后进入项目目录、安装依赖、启动项目:
pnpm install
pnpm run dev
查看项目没问题后,再安装 Pinia
:
pnpm i pinia
在 main.ts
中导入并使用 pinia
:
import { createPinia } from "pinia"
createApp(App).use(pinia).mount('#app')
创建一个 store
项目根目录下新建 store/index.ts
文件,创建一个 store。
import { defineStore } from "pinia"
export const mainStore = defineStore('main', {
})
Pinia 使用 defineStore()
来定义一个 store,它的第一个参数是 store 的命名,第二个参数是一个对象,state
等逻辑就在这个对象里面编写。如下是完整结构:
import { defineStore } from "pinia"
export const mainStore = defineStore('main', {
state: () => {},
getters: {},
actions: {}
})
state
在 store 中创建 state
,定义一个 count
返回出去:
export const mainStore = defineStore('main', {
state: () => {
return {
count: 0,
text: '文字'
}
}
})
新建 Count.vue
组件,在App.vue
中导入使用。在组件中使用 store 里面的 state
:
<script setup lang="ts">
import { mainStore } from '../store/index'
const store = mainStore()
</script>
<template>
<h1>count: {{ store.count }}</h1>
</template>
传统的结构方式,会让数据丢失响应式,Pinia 中解构 state
的值,可以使用 storeToRefs
,如下:
import { storeToRefs } from 'pinia'
// 失去响应式
// const { count, text } = store
// 不会失去响应式
const { count, text } = storeToRefs(store)
<p>解构 count:{{ count }}</p>
<p>解构 text:{{ text }}</p>
getters
getters
类似于Vue中的计算属性,有缓存机制,当它依赖的内容未发生改变时,多次读取 getters
的数据,会去读取缓存的数据,而不会每次读取都重新计算,有利于性能优化。
创建 getters
,声明 doubleCount
方法:
export const mainStore = defineStore('main', {
state: () => {
return {
count: 0,
text: '文字'
}
},
getters: {
doubleCount(state) {
console.log('读取doubleCount -------')
return state.count * 2
}
}
})
读取 getters
:
<h1>getters - doubleCount: {{ store.doubleCount }}</h1>
新建 CountButton.vue
组件,并在App.vue
中导入使用:
function handleAddCount() {
store.count++
}
<button @click="handleAddCount">count + 1</button>
点击按钮时,Count.vue
组件视图中的count
每次+1,doubleCount
会翻倍。
actions
在 actions
中编写一些逻辑,对 state
数据进行处理。
export const mainStore = defineStore('main', {
state: () => {
return {
count: 0,
// ...
}
},
getters: {
// ...
},
actions: {
addUser() {
this.count++
}
}
})
调用 actions:
<button @click="handleAddAction">调用action</button>
function handleAddAction() {
store.addUser()
}
$patch 修改状态值
要操作 state 中的值时,上面用到了在 actions
中操作和直接赋值修改 state.count = 10
,除了这两种方式以外还可以,使用 $patch
这个API。
<button @click="handlePatch1">$patch修改值</button>
function handlePatch1() {
store.$patch({
count: 99,
text: store.text === '文字' ? 'text' : '文字'
})
}
上面用了 $patch
方法直接修改了 state 的值。如果想要对数组进行增加删除数据或其他一些复杂逻辑,$patch
还可以接收一个函数用来处理复杂逻辑。如下:
function handlePatch2() {
store.$patch(state => {
if (state.count > 10) {
state.count--
} else if(state.count < 5) {
state.count++
} else {
state.count = 15
}
})
}
虽然有很多方法能够成功修改 state,但因为是全局的状态管理,所以还是尽量放在 actions 中统一进行修改,尽量不在单独的组件处随意修改。这样能使代码结构能够更加合理,可读性更强。
Setup Stores
上面创建了一个完整的 Store,用的是 Option Stores
的方式,类似于 Vue2 的Options API。Option Stores
方式创建 Store 需要将数据、函数固定的放在 state
getters
actions
中,这种方式不利于更好的组织代码。Setup Stores
类似于 Vue3 的 Composition API,能够按照功能逻辑来组织代码、灵活性更高。
创建 store/user.ts
,下面使用 Setup Stores
方式创建一个 Store。
import { defineStore } from "pinia"
export const useUserStore = defineStore('user', () => {
})
依然是使用 defineStore
方法,不同的地方是第二个参数不再是一个对象,而是一个函数。在这个函数中定义变量、方法等然后再返回出去,就可以在其他组件中使用。
import { defineStore } from "pinia"
import { computed, reactive } from "vue"
type userT = string | number
export const useUserStore = defineStore('user', () => {
// state
const count = 1
let userList: userT[] = reactive([])
// getters
const userNum = computed(() => userList.length)
// actions
function addUser(userName:userT) {
userList.push(userName)
}
function resetUser() {
userList.length = 0
}
return {
count,
userList,
userNum,
addUser,
resetUser
}
})
新建 UserList.vue
组件,在这个组件中使用 state 和 getters:
import { useUserStore } from '../store/user'
const userStore = useUserStore()
<h3>当前有 {{userStore.userNum}} 个用户 </h3>
<ul>
<li v-for="item in userStore.userList">{{ item }}</li>
</ul>
在 CountButtton.vue
中使用 actions 操作数据:
function handleAddUser() {
userStore.addUser('用户')
}
function handleResetUser() {
userStore.resetUser()
}
<button @click="handleAddUser">添加用户</button>
<button @click="handleResetUser">清空用户</button>
代码地址
转载自:https://juejin.cn/post/7154166599673446408