在 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 中。这种灵活度让代码既不会因过度简单而混乱,也不会因过度设计而臃肿。