State状态 #
什么是 State? #
State 是 Vuex store 的核心,它是应用中唯一的数据源。Vuex 使用单一状态树,这意味着一个应用仅包含一个 store 实例。
单一状态树 #
概念 #
单一状态树让我们能够直接定位任一特定的状态片段,在调试过程中也能轻易地取得整个当前应用状态的快照。
javascript
const store = createStore({
state: {
// 应用中所有的状态都在这里
user: null,
products: [],
cart: [],
settings: {}
}
})
优势 #
text
单一状态树优势
├── 集中管理 ──── 所有状态在一处管理
├── 易于调试 ──── 状态快照清晰
├── 响应式 ───── 状态变化自动更新视图
└── 可预测 ───── 状态变更可追踪
定义 State #
基本定义 #
javascript
import { createStore } from 'vuex'
const store = createStore({
state: {
// 基本类型
count: 0,
message: 'Hello Vuex',
isActive: true,
// 对象
user: {
name: 'John',
age: 25
},
// 数组
todos: [
{ id: 1, text: 'Learn Vuex', done: true },
{ id: 2, text: 'Build App', done: false }
]
}
})
初始化 State #
javascript
// 方式一:直接定义
const store = createStore({
state: {
count: 0
}
})
// 方式二:使用函数返回(推荐用于 SSR)
const store = createStore({
state() {
return {
count: 0,
user: null
}
}
})
访问 State #
在组件中访问 #
直接访问 #
vue
<template>
<div>
<p>{{ $store.state.count }}</p>
<p>{{ $store.state.user.name }}</p>
</div>
</template>
使用计算属性 #
vue
<template>
<div>
<p>{{ count }}</p>
<p>{{ userName }}</p>
</div>
</template>
<script>
export default {
computed: {
count() {
return this.$store.state.count
},
userName() {
return this.$store.state.user.name
}
}
}
</script>
mapState 辅助函数 #
基本用法 #
javascript
import { mapState } from 'vuex'
export default {
computed: {
// 使用对象展开运算符
...mapState({
// 箭头函数
count: state => state.count,
// 传字符串参数
user: 'user',
// 使用 this 访问组件局部状态
fullName(state) {
return `${state.user.firstName} ${this.localLastName}`
}
})
}
}
数组语法 #
javascript
import { mapState } from 'vuex'
export default {
computed: {
// 当映射的计算属性名称与 state 子节点名称相同时
...mapState([
'count',
'user',
'todos'
])
}
}
重命名 #
javascript
import { mapState } from 'vuex'
export default {
computed: {
...mapState({
currentCount: 'count',
currentUser: 'user'
})
}
}
组合式 API 中使用 #
Vue 3 setup 语法 #
vue
<template>
<div>
<p>{{ count }}</p>
<p>{{ user.name }}</p>
</div>
</template>
<script>
import { computed } from 'vue'
import { useStore } from 'vuex'
export default {
setup() {
const store = useStore()
return {
count: computed(() => store.state.count),
user: computed(() => store.state.user)
}
}
}
</script>
封装组合式函数 #
javascript
// composables/useUser.js
import { computed } from 'vue'
import { useStore } from 'vuex'
export function useUser() {
const store = useStore()
const user = computed(() => store.state.user)
const isLoggedIn = computed(() => store.getters.isLoggedIn)
const login = async (credentials) => {
await store.dispatch('login', credentials)
}
const logout = () => {
store.commit('SET_USER', null)
}
return {
user,
isLoggedIn,
login,
logout
}
}
vue
<!-- 使用封装的组合式函数 -->
<script>
import { useUser } from '@/composables/useUser'
export default {
setup() {
const { user, isLoggedIn, login, logout } = useUser()
return {
user,
isLoggedIn,
login,
logout
}
}
}
</script>
State 结构设计 #
扁平化设计 #
javascript
// 推荐:扁平化结构
state: {
users: {
byId: {
1: { id: 1, name: 'John' },
2: { id: 2, name: 'Jane' }
},
allIds: [1, 2]
},
posts: {
byId: {
101: { id: 101, title: 'Post 1' },
102: { id: 102, title: 'Post 2' }
},
allIds: [101, 102]
}
}
// Getter 中获取数据
getters: {
allUsers: state => state.users.allIds.map(id => state.users.byId[id]),
userById: state => id => state.users.byId[id]
}
避免深层嵌套 #
javascript
// 不推荐:深层嵌套
state: {
app: {
data: {
users: {
list: {
items: []
}
}
}
}
}
// 推荐:扁平结构
state: {
users: [],
posts: [],
comments: []
}
关系数据设计 #
javascript
state: {
// 实体数据
users: {
byId: {},
allIds: []
},
posts: {
byId: {},
allIds: []
},
// 关系映射
userPosts: {
1: [101, 102], // 用户 1 的文章
2: [103] // 用户 2 的文章
}
}
getters: {
userPosts: state => userId => {
const postIds = state.userPosts[userId] || []
return postIds.map(id => state.posts.byId[id])
}
}
State 最佳实践 #
1. 初始化所有状态 #
javascript
// 推荐:初始化所有状态
state: {
user: null,
posts: [],
loading: false,
error: null
}
// 不推荐:未初始化
state: {
user: undefined // 可能导致响应式问题
}
2. 使用常量命名 #
javascript
// 状态命名清晰
state: {
isLoading: false,
hasError: false,
errorMessage: ''
}
3. 分离关注点 #
javascript
// 按功能组织状态
state: {
// 用户相关
user: null,
isLoggedIn: false,
// 产品相关
products: [],
categories: [],
// UI 状态
loading: false,
error: null
}
常见问题 #
1. 直接修改 State #
javascript
// 错误:直接修改
this.$store.state.count = 10
// 正确:通过 mutation
this.$store.commit('SET_COUNT', 10)
2. 响应式丢失 #
javascript
// 问题:添加新属性不触发响应
this.$store.state.user.newProp = 'value'
// 解决:使用 Vue.set 或替换整个对象
// 在 mutation 中
mutations: {
ADD_PROP(state, { key, value }) {
state.user = { ...state.user, [key]: value }
}
}
3. 数组响应式问题 #
javascript
// 问题:通过索引修改
state.array[0] = newValue
// 解决:使用 splice 或替换
mutations: {
UPDATE_ARRAY(state, { index, value }) {
state.array.splice(index, 1, value)
// 或
state.array = state.array.map((item, i) =>
i === index ? value : item
)
}
}
总结 #
State 是 Vuex 的核心,掌握 State 的使用是学习 Vuex 的第一步:
- 单一状态树:所有状态集中在一处
- 响应式:状态变化自动更新视图
- mapState:简化状态映射
- 合理设计:扁平化、初始化、清晰命名
继续学习 mapState辅助函数,深入了解状态映射技巧。
最后更新:2026-03-28