人人都会AI编程

State、Getters、Actions 三大核心概念

更新时间:2026-07-09

Pinia 把状态管理拆成三个清晰的角色:State(状态)Getters(获取器)Actions(动作)。它们各司其职,组合在一起就能覆盖从简单计数器到复杂业务逻辑的所有场景。相比 Vuex 的 Mutations + Actions 分离设计,Pinia 去掉了 Mutations,读写状态更直接,概念更少,写起来也更自然。


State:数据的家

State 就是存储数据的地方。在 Pinia 中,定义 State 就像在组件中定义 data,只不过它的数据是全局共享的——任何组件都可以访问和修改它。

定义方式(选项式 Store):

import { defineStore } from 'pinia'

export const useUserStore = defineStore('user', {
  state: () => ({
    name: '',
    isLoggedIn: false,
    cartItems: []
  })
})

组合式 Store 写法(更推荐):

import { ref, reactive } from 'vue'
import { defineStore } from 'pinia'

export const useUserStore = defineStore('user', () => {
  const name = ref('')
  const isLoggedIn = ref(false)
  const cartItems = ref([])

  return { name, isLoggedIn, cartItems }
})

在组件中使用

const userStore = useUserStore()
console.log(userStore.name)        // 读取状态
userStore.name = 'Alice'          // 直接修改状态
userStore.$patch({ name: 'Bob' }) // 批量修改状态

关键点

  • State 默认就是响应式的,用 ref / reactive 包裹,或选项式返回一个对象。
  • 修改 State 不需要通过 mutations,直接赋值即可,Pinia 内部会自动触发依赖该状态的组件更新。
  • 如果你需要一次修改多个属性,推荐使用 $patch,它能减少响应式通知次数,也方便 DevTools 追踪变更。

Getters:计算属性,带缓存

Getters 用来从 State 中派生数据,类似于组件里的 computed。它们的特点是有缓存,只有当依赖的 State 改变时才会重新计算。

定义方式

export const useCartStore = defineStore('cart', () => {
  const items = ref([])

  // Getter:计算总价
  const totalPrice = computed(() => {
    return items.value.reduce((sum, item) => sum + item.price * item.qty, 0)
  })

  // Getter 可以依赖其他 Getter
  const discountPrice = computed(() => {
    return totalPrice.value > 100 ? totalPrice.value * 0.9 : totalPrice.value
  })

  return { items, totalPrice, discountPrice }
})

在组件中使用

const cartStore = useCartStore()
console.log(cartStore.totalPrice) // 像访问普通属性一样使用

实用建议

  • 如果一个数据可以从 State 算出来,就把它放进 Getter,而不是存在 State 里重复维护。
  • Getter 可以接受参数,但必须返回一个函数(注意这样会失去缓存能力,每次调用都会重新执行)。
  • Getter 在 DevTools 中会以独立节点展示,便于调试。

Actions:业务逻辑的载体

Actions 是定义在 Store 中的方法,用来封装业务逻辑、修改 State、调用接口等。它们是 Pinia 中改变 State 的主要途径(尤其当修改需要经过一些异步步骤或多步骤处理时)。

定义方式

export const useUserStore = defineStore('user', () => {
  const name = ref('')
  const isLoggedIn = ref(false)

  // 同步 Action
  function login(username) {
    name.value = username
    isLoggedIn.value = true
  }

  // 异步 Action
  async function fetchProfile() {
    try {
      const res = await api.getProfile()
      name.value = res.name
      isLoggedIn.value = true
    } catch (err) {
      console.error('获取用户信息失败', err)
    }
  }

  function logout() {
    name.value = ''
    isLoggedIn.value = false
  }

  return { name, isLoggedIn, login, logout, fetchProfile }
})

在组件中使用

const userStore = useUserStore()
userStore.login('Alice')      // 调用同步 Action
await userStore.fetchProfile() // 调用异步 Action

为什么推荐用 Actions 修改 State

  • 逻辑集中:把“登录”这种完整操作封到一个函数,组件只需调用它,不必关心内部改了哪些状态。
  • 方便追踪:DevTools 能记录 Action 的调用时机、参数和状态变更快照,直接赋值虽然也能用,但调试体验不如 Actions。
  • 异步支持天然:Actions 里 async/await 用起来毫无负担,不需要像 Vuex 一样专门区分 Mutations 和 Actions。
  • 可复用:Store 之间可以互相调用 Actions,比如 useCartStore().clearCart() 在结算完成后由订单 Store 调用。

三者关系一言蔽之

  • State 是“仓库里有什么”。
  • Getters 是“我想怎么看这些货”(汇总、过滤、排序)。
  • Actions 是“怎么入库、出库、盘点”(业务操作,可同步可异步)。

这种分层让状态管理变得清晰:组件只管触发 Actions 和读取 Getters,State 的维护细节藏在 Store 内部。需要加个“打印日志”或“失败重试”,改 Actions 就行,完全不影响组件。