Pinia 状态管理
官网: https://pinia.vuejs.org/zh/
Pinia
是Vue3
官方推荐的状态管理库, 可以看作是 Vuex 的替代品
Pinia
提供了更简单、更直观的API
和更好的TypeScript
支持
核心特点
- 更简单的
API
: 相比Vuex
更简洁直观 - 组合式
API
友好: 完美支持Vue 3
的组合式API
TypeScript
支持: 提供完整的类型推断- 模块化设计: 不需要嵌套模块, 每个
store
都是独立的 - 轻量级: 体积小, 压缩后约
1KB
DevTools
支持: 集成Vue DevTools
基本配置
安装
bash
# 安装 pinia
npm install pinia
配置
- 修改
src/main.ts
ts
import { createApp } from 'vue'
import App from './App.vue'
// 引入 createPinia, 用于创建 pinia
import { createPinia } from 'pinia'
// 创建 pinia
const pinia = createPinia()
// 挂载使用
createApp(App).use(pinia).mount('#app')
基本使用
Store
是一个保存状态、业务逻辑的实体, 每个组件都可以读取、写入
Store
有三个概念
state
: 相当于组件中的data
getter
: 相当于组件中的computed
action
: 相当于组件中的methods
在src
目录下新建store
文件夹用于存放状态管理文件
- 实现效果
存储和读取数据
src/store/count.ts
用于Count.vue
组件
ts
// 引入 defineStore 用于创建 Store
import { defineStore } from 'pinia'
// 定义并暴露一个 Store
export const useCountStore = defineStore('count', {
// 动作
actions: {},
// 状态
state() {
return {
sum: 6,
school: '坐忘道'
}
},
// 计算
getters: {}
})
src/store/talk.ts
用于Talk.vue
组件
ts
// 引入 defineStore 用于创建 Store
import { defineStore } from 'pinia'
// 定义并暴露一个 Store
export const useTalkStore = defineStore('talk', {
// 动作
actions: {},
// 状态
state() {
return {
talkList: [
{id: 'yuysada01', content: '你今天有点怪,哪里怪?怪好看的!'},
{id: 'yuysada02', content: '草莓、蓝莓、蔓越莓,你想我了没?'},
{id: 'yuysada03', content: '心里给你留了一块地,我的死心塌地'}
]
}
},
// 计算
getters: {}
})
在组件中使用state
中的数据
Count.vue
组件
vue
<template>
<h2>当前求和为: {{ countStore.sum }}</h2>
</template>
<script setup lang="ts" name="Count">
// 引入对应的 useXxxxxStore
import { useCountStore } from '@/store/count'
// 调用 useXxxxxStore 得到对应的 store
const countStore = useCountStore()
</script>
Talk.vue
组件
vue
<template>
<ul>
<li v-for="talk in talkStore.talkList" :key="talk.id">
{{ talk.content }}
</li>
</ul>
</template>
<script setup lang="ts" name="Talk">
import axios from 'axios'
import { useTalkStore } from '@/store/talk'
const talkStore = useTalkStore()
</script>
修改数据
修改数据有三种方式
1. 直接修改:
ts
import { useCountStore } from '@/store/sum'
const countStore = useSumStore()
// 直接修改
countStore.sum = 666
2. 批量修改: 可以同时修改多个
ts
import { useCountStore } from '@/store/sum'
const countStore = useSumStore()
// 批量修改
countStore.$patch({
sum: 999,
school: '家里蹲'
})
3. 借助 action 修改: action
中可以编写一些业务逻辑
src/store/count.ts
在action
中编写业务逻辑
ts
import { defineStore } from 'pinia'
export const useCountStore = defineStore('count', {
// ...
actions: {
// 加
increment(value: number) {
if (this.sum < 10) {
// 操作 countStore 中的 sum
this.sum += value
}
},
// 减
decrement(value: number){
if(this.sum > 1){
this.sum -= value
}
}
},
// ...
})
- 组件中调用
action
ts
import { useCountStore } from '@/store/sum'
const countStore = useSumStore()
// 调用对应 action
countStore.increment(1)
storeToRefs
借助storeToRefs
将store
中的数据转为ref
对象, 方便在模板中使用
注意
pinia
提供的storeToRefs
只会将数据做转换, 而Vue
的toRefs
会转换store
中数据
- 代码示例
vue
<template>
<div class="count">
<h2>当前求和为: {{ sum }}</h2>
</div>
</template>
<script setup lang="ts" name="Count">
import { useCountStore } from '@/store/count'
// 从 pinia 中引入 storeToRefs
import { storeToRefs } from 'pinia'
// 得到 countStore
const countStore = useCountStore()
// 使用 storeToRefs 转换 countStore, 然后解构
const { sum } = storeToRefs(countStore)
</script>
getters
当state
中的数据需要经过处理后再使用时, 可以使用getters
配置
- 追加
getters
配置
ts
// 引入 defineStore 用于创建 Store
import { defineStore } from 'pinia'
// 定义并暴露一个 Store
export const useCountStore = defineStore('count', {
// 动作
actions: {},
// 状态
state() {
return {
sum: 1,
school: 'Miller'
}
},
// 计算
getters: {
bigSum: (state) :number => state.sum * 10,
upperSchool() :string {
return this.school.toUpperCase()
}
}
})
- 组件中读取数据
ts
import { useCountStore } from '@/store/sum'
const countStore = useSumStore()
// 获取 actions
const { increment, decrement } = countStore
// 获取 getters
let { sum, school, bigSum, upperSchool } = storeToRefs(countStore)
$subscribe
通过store
的$subscribe()
方法侦听state
及其变化
- 代码示例
ts
import { useTalkStore } from '@/store/talk'
const talkStore = useTalkStore()
talkStore.$subscribe((mutate, state) => {
console.log('LoveTalk', mutate, state)
localStorage.setItem('talk', JSON.stringify(talkList.value))
})
store 组合式写法
- 代码示例
ts
import { defineStore } from 'pinia'
import axios from 'axios'
import { nanoid } from 'nanoid'
import { reactive } from 'vue'
export const useTalkStore = defineStore('talk', () => {
// talkList 就是 state
const talkList = reactive(
JSON.parse(localStorage.getItem('talkList') as string) || []
)
// getATalk 函数相当于 action
async function getATalk() {
// 发请求, 下面这行的写法是: 连续解构赋值 + 重命名
let { data: { content: title } } = await axios.get('https://api.uomg.com/api/rand.qinghua?format=json')
// 把请求回来的字符串,包装成一个对象
let obj = { id: nanoid(), title }
// 放到数组中
talkList.unshift(obj)
}
return { talkList, getATalk }
})