Web前端笔记-19、Pinia-defineStore、state、action、getter、异步action、storeToRefs工具函数、调试

状态管理是什么?

在前端开发中,“状态” (State) 指的就是驱动应用运行的数据。这包括了用户信息、从服务器获取的文章列表、UI 元素的是否可见(例如一个模态框是打开还是关闭状态)等等。

状态管理,顾名思义,就是一套管理和维护这些共享应用状态的模式和工具。它提供了一个明确的结构,让你能够以一种可预测、可维护的方式来读取和修改应用中的共享数据。

为什么需要状态管理?

当应用变得越来越复杂时,你可能会遇到以下情况:

  • 多组件共享数据:多个不相关的组件可能都需要访问或修改同一份数据(例如,用户的登录状态,在导航栏、个人中心页、文章发布页可能都需要用到)。
  • 组件间通信复杂:Vue 的父子组件通信很简单 (props/emit),但当组件层级很深,或者兄弟组件、远房亲戚组件之间需要通信时,通过 props 一层一层地传递数据(这个过程被称为“属性钻探” (Prop Drilling))会变得极其繁琐和难以维护。

如果没有一个集中的状态管理方案,数据会散落在各个组件内部,导致:

  • 数据流混乱:你很难追踪数据的变化是由哪个组件引起的,也很难调试 Bug。
  • 数据不一致:同一个数据在不同组件中可能存在多份拷贝,当一份数据更新时,很容易忘记同步更新其他地方,导致用户看到的数据不一致。
  • 代码难以维护:组件之间耦合度太高,修改一个组件可能会无意中影响到其他组件。

状态管理(Pinia)解决了什么问题?

Pinia 这类状态管理库的核心目标就是解决上述问题,具体来说:

  1. 提供单一数据源 (Single Source of Truth)
    • Pinia 将所有需要共享的状态都集中存放在一个地方(称为 Store)。任何组件都可以直接从这个 Store 中读取数据,而不需要通过父组件层层传递。这确保了应用中所有部分看到的数据都是一致的。
  2. 解决“属性钻探” (Prop Drilling)
    • 有了集中的 Store,任何深层嵌套的组件都可以轻松地访问到全局状态,彻底告别了繁琐的props传递。
  3. 提供可预测的状态变更
    • Pinia 提倡通过定义好的actions(方法) 来修改状态。这使得状态的每一次变更都是明确且可追踪的。当出现问题时,你可以快速定位是哪个action导致了状态异常。配合 Vue Devtools,你可以清晰地看到状态变化的历史记录。
  4. 解耦组件
    • 组件不再需要直接关心数据来自哪个组件,它们只需要和中央的 Store 进行交互。这大大降低了组件之间的耦合度,让代码更模块化,也更容易进行测试和重构。

简单来说,Pinia 就像为你复杂的 Vue 应用开设了一个“中央银行”。各个组件不用再互相“借钱”(传递数据),而是直接从这个“银行”(Store)存取数据,一切都变得井然有序。

pinia用法:定义Store+使用Store

Pinia是Vue的专属的最新状态管理库,是Vuex状态管理工具的替代品。

  1. 提供更加简单的APl(去掉了mutation)
  2. 提供符合组合式风格的API(和Vue3新语法统一)
  3. 去掉了modules的概念,每一个store都是一个独立的模块
  4. 配合TypeScript更加友好,提供可靠的类型推断

npm create vite@latest vue-pinia-ts -- --template vue-ts

用法:

  1. 定义store
  2. 组件使用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的主要作用就是:

  1. 对 state 中的数据进行计算或处理后返回新值。
  2. 这个返回值会被缓存,只有当它依赖的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的调试支持。

posted @ 2025-06-02 18:21  subeipo  阅读(198)  评论(0)    收藏  举报