4、State核心:定义State、访问State、重置State、批量修改State($patch)、State的响应式解构
State 是 Pinia 的心脏。说白了,它就是存放数据的地方。
我刚开始用 Vuex 的时候,总觉得 State 的定义有点绕。到了 Pinia,嗯,清爽多了。它就是一个函数,返回一个对象,对象里放着你所有的状态。
4.1 定义 State
在 Pinia 里定义 State,你只需要在 defineStore 里写一个 state 函数。这个函数返回一个对象,对象里的每个属性就是你的状态。
import { defineStore } from 'pinia'
export const useUserStore = defineStore('user', {
state: () => ({
name: '张三',
age: 25,
isLoggedIn: false,
hobbies: ['coding', 'reading'],
address: {
city: '北京',
district: '海淀'
}
})
})
我个人习惯把 State 写得尽量扁平。但有些场景下,嵌套对象确实更合理,比如用户信息里的地址。这时候要注意,Pinia 对嵌套对象的响应式处理是没问题的,但修改时最好用 $patch,后面会讲。
4.2 访问 State
访问 State 有两种方式。一种是直接通过 store 实例访问,另一种是通过 storeToRefs 解构。
方式一:直接访问
const userStore = useUserStore()
console.log(userStore.name) // '张三'
console.log(userStore.age) // 25
这种方式最简单,也最常用。我在项目中大部分场景都用这种方式。但要注意,直接访问会丢失响应式吗?不会。因为 store 本身就是一个 reactive 对象。
方式二:通过 storeToRefs 解构
import { storeToRefs } from 'pinia'
const userStore = useUserStore()
const { name, age } = storeToRefs(userStore)
console.log(name.value) // '张三'
console.log(age.value) // 25
这里有个坑。如果你直接用解构赋值 const { name } = userStore,name 会丢失响应式。为什么?因为解构出来的只是一个普通的值,不是 ref。而 storeToRefs 会把每个属性都转成 ref,这样解构出来就是响应式的了。
const { name } = userStore 这种方式解构 State。我曾经在项目里踩过这个坑,数据变了页面不更新,排查了半天才发现是解构的问题。
4.3 重置 State
重置 State 是 Pinia 的一个亮点。你只需要调用 $reset() 方法,所有状态就会回到初始值。
const userStore = useUserStore()
// 修改状态
userStore.name = '李四'
userStore.age = 30
// 重置
userStore.$reset()
console.log(userStore.name) // '张三'
console.log(userStore.age) // 25
这个功能在表单场景下特别实用。比如用户填写了一半想重新填,直接调用 $reset() 就行。我之前做后台管理系统时,每个表单页面都用了这个功能,省了不少代码。
但要注意,$reset() 只能重置通过 state 函数定义的初始值。如果你在 store 里动态添加了属性,它不会重置这些属性。
4.4 批量修改 State($patch)
当你需要同时修改多个状态时,一个个赋值太麻烦了。这时候 $patch 就派上用场了。
方式一:传入一个对象
userStore.$patch({
name: '王五',
age: 28,
isLoggedIn: true
})
方式二:传入一个函数
userStore.$patch((state) => {
state.name = '王五'
state.age = 28
state.isLoggedIn = true
state.hobbies.push('swimming')
})
这两种方式有什么区别?
| 方式 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| 对象方式 | 简单属性修改 | 代码简洁 | 不能处理复杂逻辑 |
| 函数方式 | 需要逻辑处理或操作数组 | 灵活,可以写任意逻辑 | 代码稍多 |
我个人更推荐函数方式。为什么?因为有时候你需要根据当前状态做判断,或者操作数组。比如上面的 hobbies.push('swimming'),用对象方式就做不到。
4.5 State 的响应式解构
前面提到了 storeToRefs,这里再深入讲一下。
响应式解构的核心就是 storeToRefs。它会把 store 里的每个属性都转成 ref,这样你解构出来就能保持响应式。
import { storeToRefs } from 'pinia'
const userStore = useUserStore()
const { name, age, hobbies } = storeToRefs(userStore)
// 在模板中使用
// <div>{{ name }}</div>
// <div>{{ age }}</div>
// 修改时要用 .value
name.value = '赵六'
age.value = 32
这里有个细节要注意:storeToRefs 只会解构 State 和 Getter,不会解构 Action。如果你解构了一个 Action,它不会报错,但会失去绑定,导致 this 指向错误。
this 指向了 undefined。排查了半天才发现是解构的问题。记住,Action 不要解构,直接通过 store 调用。
还有一个场景:如果你只需要某个 State 的一部分,比如只需要 address.city,怎么办?
const { address } = storeToRefs(userStore)
// 这样 address 是一个 ref,address.value.city 是响应式的
// 或者直接解构嵌套属性
const city = computed(() => userStore.address.city)
我个人更推荐用 computed 来处理嵌套属性。因为 storeToRefs 解构出来的 address 是整个对象,如果你只想要 city,用 computed 更精确。
4.6 总结
State 是 Pinia 的基础,掌握好这几点就够了:
- 定义:用 state 函数返回初始值
- 访问:直接通过 store 实例,或者用 storeToRefs 解构
- 重置:调用 $reset() 回到初始状态
- 批量修改:用 $patch,推荐函数方式
- 响应式解构:用 storeToRefs,别直接解构
下一章我们会讲 Getter,也就是 Pinia 里的计算属性。到时候你会发现,Getter 和 State 配合起来,能写出非常优雅的代码。