Web前端笔记-19、Pinia-defineStore、state、action、getter、异步action、storeToRefs工具函数、调试
状态管理是什么?
在前端开发中,“状态” (State) 指的就是驱动应用运行的数据。这包括了用户信息、从服务器获取的文章列表、UI 元素的是否可见(例如一个模态框是打开还是关闭状态)等等。
状态管理,顾名思义,就是一套管理和维护这些共享应用状态的模式和工具。它提供了一个明确的结构,让你能够以一种可预测、可维护的方式来读取和修改应用中的共享数据。
为什么需要状态管理?
当应用变得越来越复杂时,你可能会遇到以下情况:
- 多组件共享数据:多个不相关的组件可能都需要访问或修改同一份数据(例如,用户的登录状态,在导航栏、个人中心页、文章发布页可能都需要用到)。
- 组件间通信复杂:Vue 的父子组件通信很简单 (props/emit),但当组件层级很深,或者兄弟组件、远房亲戚组件之间需要通信时,通过 props 一层一层地传递数据(这个过程被称为“属性钻探” (Prop Drilling))会变得极其繁琐和难以维护。
如果没有一个集中的状态管理方案,数据会散落在各个组件内部,导致:
- 数据流混乱:你很难追踪数据的变化是由哪个组件引起的,也很难调试 Bug。
- 数据不一致:同一个数据在不同组件中可能存在多份拷贝,当一份数据更新时,很容易忘记同步更新其他地方,导致用户看到的数据不一致。
- 代码难以维护:组件之间耦合度太高,修改一个组件可能会无意中影响到其他组件。
状态管理(Pinia)解决了什么问题?
Pinia 这类状态管理库的核心目标就是解决上述问题,具体来说:
- 提供单一数据源 (Single Source of Truth)
- Pinia 将所有需要共享的状态都集中存放在一个地方(称为 Store)。任何组件都可以直接从这个 Store 中读取数据,而不需要通过父组件层层传递。这确保了应用中所有部分看到的数据都是一致的。
- 解决“属性钻探” (Prop Drilling)
- 有了集中的 Store,任何深层嵌套的组件都可以轻松地访问到全局状态,彻底告别了繁琐的
props传递。
- 有了集中的 Store,任何深层嵌套的组件都可以轻松地访问到全局状态,彻底告别了繁琐的
- 提供可预测的状态变更
- Pinia 提倡通过定义好的
actions(方法) 来修改状态。这使得状态的每一次变更都是明确且可追踪的。当出现问题时,你可以快速定位是哪个action导致了状态异常。配合 Vue Devtools,你可以清晰地看到状态变化的历史记录。
- Pinia 提倡通过定义好的
- 解耦组件
- 组件不再需要直接关心数据来自哪个组件,它们只需要和中央的 Store 进行交互。这大大降低了组件之间的耦合度,让代码更模块化,也更容易进行测试和重构。
简单来说,Pinia 就像为你复杂的 Vue 应用开设了一个“中央银行”。各个组件不用再互相“借钱”(传递数据),而是直接从这个“银行”(Store)存取数据,一切都变得井然有序。
pinia用法:定义Store+使用Store
Pinia是Vue的专属的最新状态管理库,是Vuex状态管理工具的替代品。
- 提供更加简单的APl(去掉了mutation)
- 提供符合组合式风格的API(和Vue3新语法统一)
- 去掉了modules的概念,每一个store都是一个独立的模块
- 配合TypeScript更加友好,提供可靠的类型推断
npm create vite@latest vue-pinia-ts -- --template vue-ts
用法:
- 定义store
- 组件使用store
定义Store
- 引入defineStore方法,使用
export const Store名称 = defineStore('模块名'), ()=>{}) - 定义store:state和action
- state:响应式数据
- action:修改数据的方法
- state和action以对象形式return
组件使用Store
- 从
@/stores/xxx.ts中引入{ Store名称 } const xxx = Store名称()得到对象, 使用xxx.数据 xxx.方法
getters
getters 就像是 Store 的计算属性 (computed****)。
简单来说,getters的主要作用就是:
- 对 state 中的数据进行计算或处理后返回新值。
- 这个返回值会被缓存,只有当它依赖的
state数据发生变化时,才会重新计算。
getters和actions的区别
| 特性 | Getters | 普通方法 (Actions) |
|---|---|---|
| 主要用途 | 派生状态(从现有 state 计算出新值)。 | 执行逻辑、修改状态(通常包含异步操作)。 |
| 调用方式 | 像属性一样访问 | 像方法一样调用 |
| 参数传递 | 默认不接受参数。若要传参,需让 getter 返回一个函数,但这会使其失去缓存。 | 可以直接、方便地接受任意数量和类型的参数。 |
| 修改 State | 不应该直接修改state。Getters 的职责是“读取”和“计算”,而不是“写入”。 |
可以并且通常用来修改state(例如this.count++)。这是修改状态的推荐方式。 |
Pinia中的getters直接使用computed函数进行模拟,并把getter return出去。
const xxx = computed( ()=>{} )
action异步
与普通axios异步方法一样。
// countStore.ts
// 计数器 store
import axios from 'axios'
import type { ChannelRes, ChannelItem } from '../types/data.d.ts'
import { defineStore } from 'pinia'
import { computed, ref } from 'vue'
export const useCounterStore = defineStore('counter', () => {
// state
const count = ref(0)
// action
const increment = () => {
count.value++
}
// getter
const doubleCount = computed(() => count.value * 2)
// 异步action axios
const list = ref<ChannelItem[]>([])
const getList = async () => {
const res = await axios.request<ChannelRes>({
url: '',
})
list.value = res.data.data.channels
}
// 使用
import { useCounterStore } from './stores/counterStore'
const counterStore = useCounterStore()
storeToRefs工具函数
使用storeToRefs函数可以辅助保持数据(state+getter)的响应式解构。
只支持state getter,如果是action直接解构即可。
Store return出去的是一个对象,在组件中使用也是 xxx.xxx 使用。
如果直接解构使用,会丢失响应性。
使用storeToRefs函数保持响应性。
const {count, doubleCount} = storeToRefs(counterStore)
<script setup lang="ts">
import { useCounterStore } from './stores/counterStore'
import { storeToRefs } from 'pinia'
const countStore = useCounterStore()
const { count, doubleCount } = storeToRefs(countStore)
const { increment } = countStore
</script>
<template>
<div>
<div>两倍:{{ doubleCount }}</div>
<button @click="increment">{{ count }}</button>
</div>
</template>
调试
dev tools内置对pinia的调试支持。

浙公网安备 33010602011771号