人人都会AI编程

修改状态的两种方式:直接修改与 Actions 封装

更新时间:2026-07-09

在 Pinia 中,修改 Store 里的状态有两种截然不同的方式:直接修改 state通过 actions 封装修改逻辑。它们不是互斥的,而是适用于不同复杂度的场景。

方式一:直接修改 state

Pinia 的 Store 实例本身就是响应式对象,你可以在组件中直接读写 state 的属性,就像操作一个普通的 JavaScript 对象一样。

// store/counter.js
import { defineStore } from 'pinia'

export const useCounterStore = defineStore('counter', {
  state: () => ({
    count: 0,
    user: { name: 'Alice' }
  })
})
<template>
  <div>
    <p>计数:{{ counter.count }}</p>
    <button @click="counter.count++">+1</button>
    <button @click="counter.user.name = 'Bob'">改名</button>
  </div>
</template>

<script setup>
import { useCounterStore } from '@/store/counter'
const counter = useCounterStore()
</script>

特点与适用场景

  • 简洁直观:没有额外的模板代码,尤其适合简单的状态更新(如计数器增减、开关切换)。
  • 性能无损:Pinia 内部依然是响应式代理,直接修改同样会触发视图更新,不会丢失响应性。
  • 适合“原子性”操作:当操作只是简单的赋值或递增,且不涉及业务逻辑时,直接修改非常干净。

对比 Vuex:在 Vuex 中,严格模式下直接修改 state 会报错,必须通过 mutations;Pinia 移除了 mutations 概念,让写法更自由。

方式二:通过 actions 封装修改

当状态修改伴随着业务逻辑、异步请求、数据校验或多重状态变更时,直接把逻辑散落在组件里会让代码难以维护。这时应将修改流程封装到 Store 的 actions 中。

// store/user.js
import { defineStore } from 'pinia'
import { userLoginApi } from '@/api/user'

export const useUserStore = defineStore('user', {
  state: () => ({
    token: '',
    userInfo: null,
    isLoading: false
  }),
  actions: {
    async login(username, password) {
      this.isLoading = true
      try {
        const res = await userLoginApi({ username, password })
        this.token = res.token
        this.userInfo = res.user
        // 还可附带埋点、缓存等操作
      } finally {
        this.isLoading = false
      }
    },
    logout() {
      this.token = ''
      this.userInfo = null
    }
  }
})
<template>
  <div>
    <button @click="handleLogin" :disabled="user.isLoading">
      {{ user.isLoading ? '登录中...' : '登录' }}
    </button>
  </div>
</template>

<script setup>
import { useUserStore } from '@/store/user'
const user = useUserStore()

async function handleLogin() {
  await user.login('admin', '123456')
  // 登录成功后组件再执行跳转等操作
}
</script>

核心价值

  • 业务逻辑内聚:组件只需调用 user.login(),不需要关心内部的异步流程、错误处理和中间状态。
  • 可复用与可测试:多个组件触发登录时,都在调用同一个 action,逻辑一致;action 可以独立测试,不依赖组件环境。
  • 维护性:当登录流程需要增加图形验证码校验、日志上报、token 刷新等逻辑时,修改只发生在 action 内部,不影响组件。

选型建议

| 场景 | 推荐方式 |
|------|----------|
| 简单的值更新(如 count++、开关状态切换) | 直接修改 state |
| 单一状态赋值(如填写表单字段) | 直接修改 state |
| 涉及异步请求(如登录、数据提交) | actions 封装 |
| 多个状态联动更新(如清空购物车同时重置优惠) | actions 封装 |
| 需要调用外部工具函数或触发副作用 | actions 封装 |

真实项目中,两种方式通常混合使用:大部分表单状态绑定、UI 开关等直接用直接修改保持简洁;而网络请求、复杂业务流程则全部收入 actions 中。这种灵活度让代码既不会因过度简单而混乱,也不会因过度设计而臃肿。