State 状态 #
定义 State #
State 是 Store 的数据源,必须是一个返回初始状态的函数:
ts
export const useUserStore = defineStore('user', {
state: () => ({
name: 'John',
age: 25,
email: 'john@example.com',
preferences: {
theme: 'dark',
language: 'zh-CN'
}
})
})
为什么是函数? #
使用函数确保每个 Store 实例都有独立的状态副本:
ts
// 如果是对象,所有实例共享同一引用
state: {
name: 'John' // 错误!
}
// 使用函数,每个实例独立
state: () => ({
name: 'John' // 正确!
})
TypeScript 类型 #
ts
interface UserState {
name: string
age: number
email: string
preferences: {
theme: 'dark' | 'light'
language: string
}
}
export const useUserStore = defineStore('user', {
state: (): UserState => ({
name: 'John',
age: 25,
email: 'john@example.com',
preferences: {
theme: 'dark',
language: 'zh-CN'
}
})
})
访问 State #
直接访问 #
vue
<template>
<div>
<p>姓名: {{ userStore.name }}</p>
<p>年龄: {{ userStore.age }}</p>
</div>
</template>
<script setup>
import { useUserStore } from '@/stores/user'
const userStore = useUserStore()
</script>
解构访问 #
直接解构会失去响应性:
ts
const userStore = useUserStore()
// 错误:失去响应性
const { name, age } = userStore
使用 storeToRefs 保持响应性:
vue
<script setup>
import { storeToRefs } from 'pinia'
import { useUserStore } from '@/stores/user'
const userStore = useUserStore()
// 正确:保持响应性
const { name, age } = storeToRefs(userStore)
// Actions 不需要 storeToRefs
const { updateName } = userStore
</script>
Setup Store 中的访问 #
ts
export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
// 直接使用 .value
function increment() {
count.value++
}
return { count, increment }
})
修改 State #
直接修改 #
ts
const userStore = useUserStore()
// 直接修改单个属性
userStore.name = 'Jane'
userStore.age = 26
使用 $patch #
$patch 用于批量修改状态:
ts
// 对象形式
userStore.$patch({
name: 'Jane',
age: 26,
email: 'jane@example.com'
})
// 函数形式(适合复杂逻辑)
userStore.$patch((state) => {
state.name = 'Jane'
state.age++
state.preferences.theme = 'light'
})
通过 Actions 修改(推荐) #
ts
export const useUserStore = defineStore('user', {
state: () => ({
name: '',
age: 0,
email: ''
}),
actions: {
updateProfile(data: { name: string; age: number; email: string }) {
this.name = data.name
this.age = data.age
this.email = data.email
}
}
})
vue
<script setup>
import { useUserStore } from '@/stores/user'
const userStore = useUserStore()
// 通过 action 修改
userStore.updateProfile({
name: 'Jane',
age: 26,
email: 'jane@example.com'
})
</script>
重置 State #
使用 $reset() 将状态重置为初始值:
ts
const userStore = useUserStore()
// 修改状态
userStore.name = 'Jane'
userStore.age = 30
// 重置到初始状态
userStore.$reset()
// name: '', age: 0
Setup Store 的重置 #
Setup Store 需要手动实现 $reset:
ts
export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
function $reset() {
count.value = 0
}
return { count, $reset }
})
替换 State #
使用 $state 完全替换状态:
ts
const userStore = useUserStore()
userStore.$state = {
name: 'New User',
age: 30,
email: 'new@example.com',
preferences: {
theme: 'light',
language: 'en-US'
}
}
订阅 State 变化 #
$subscribe #
监听状态变化:
ts
const userStore = useUserStore()
userStore.$subscribe((mutation, state) => {
// mutation.type: 'direct' | 'patch object' | 'patch function'
// mutation.storeId: store 的 id
// mutation.payload: $patch 的参数(仅 patch object 时)
console.log('Type:', mutation.type)
console.log('Store ID:', mutation.storeId)
console.log('New State:', state)
// 持久化到 localStorage
localStorage.setItem('user', JSON.stringify(state))
})
订阅选项 #
ts
userStore.$subscribe((mutation, state) => {
// ...
}, {
// 是否在组件卸载后继续监听
detached: true,
// 立即执行一次
immediate: true
})
全局订阅 #
监听所有 Store 的变化:
ts
// main.ts
const pinia = createPinia()
pinia.use(({ store }) => {
store.$subscribe((mutation, state) => {
console.log(`Store ${mutation.storeId} changed`)
})
})
组合 State #
访问其他 Store #
ts
export const useCartStore = defineStore('cart', {
state: () => ({
items: []
}),
getters: {
// 在 getter 中访问其他 store
userName(): string {
const userStore = useUserStore()
return userStore.name
}
},
actions: {
// 在 action 中访问其他 store
checkout() {
const userStore = useUserStore()
console.log(`Checking out for ${userStore.name}`)
}
}
})
共享 State #
多个 Store 共享部分状态:
ts
// stores/shared.ts
import { ref } from 'vue'
export const sharedTheme = ref('dark')
ts
// stores/user.ts
import { defineStore } from 'pinia'
import { sharedTheme } from './shared'
export const useUserStore = defineStore('user', () => {
const theme = sharedTheme
function toggleTheme() {
theme.value = theme.value === 'dark' ? 'light' : 'dark'
}
return { theme, toggleTheme }
})
ts
// stores/admin.ts
import { defineStore } from 'pinia'
import { sharedTheme } from './shared'
export const useAdminStore = defineStore('admin', () => {
const theme = sharedTheme
return { theme }
})
嵌套 State #
响应式更新嵌套对象 #
ts
export const useUserStore = defineStore('user', {
state: () => ({
profile: {
name: 'John',
address: {
city: 'Beijing',
country: 'China'
}
}
})
})
ts
// 直接修改嵌套属性
userStore.profile.address.city = 'Shanghai'
// 使用 $patch
userStore.$patch((state) => {
state.profile.address.city = 'Shanghai'
})
深拷贝 State #
使用 $state 获取深拷贝:
ts
const userStore = useUserStore()
// 获取深拷贝
const stateCopy = JSON.parse(JSON.stringify(userStore.$state))
// 或者使用 lodash
import { cloneDeep } from 'lodash'
const copy = cloneDeep(userStore.$state)
State 最佳实践 #
1. 初始化所有属性 #
ts
// 推荐
state: () => ({
user: null, // 明确初始化
loading: false,
error: null
})
// 不推荐
state: () => ({
// user 未定义,可能导致错误
})
2. 使用 TypeScript #
ts
interface User {
id: number
name: string
email: string
}
interface UserState {
user: User | null
loading: boolean
error: string | null
}
export const useUserStore = defineStore('user', {
state: (): UserState => ({
user: null,
loading: false,
error: null
})
})
3. 通过 Actions 修改 #
ts
// 推荐:通过 action 修改
userStore.updateName('Jane')
// 不推荐:直接修改(除非简单场景)
userStore.name = 'Jane'
4. 使用 $patch 批量修改 #
ts
// 推荐:批量修改使用 $patch
userStore.$patch({
name: 'Jane',
age: 26,
email: 'jane@example.com'
})
// 不推荐:多次单独修改
userStore.name = 'Jane'
userStore.age = 26
userStore.email = 'jane@example.com'
下一步 #
现在你已经掌握了 State 的使用,接下来让我们学习 Getters。
- Getters计算属性 - 派生状态处理
最后更新:2026-03-28