Vuex 是 Vue 官方提供的状态管理模式,用于集中管理 Vue 应用中多个组件共享的状态(如用户信息、购物车数据等),解决组件间复杂的状态传递和共享问题。其核心思想是 “单一状态树”,将所有共享状态存储在一个集中的仓库(Store)中,通过规范的方式(Mutation、Action 等)修改状态,保证状态变更的可追踪性。
一、核心概念与工作流程
Vuex 包含 5 个核心部分,工作流程如下:组件 → dispatch(Action) → commit(Mutation) → 修改 State → 组件重新渲染
| 核心部分 | 作用 | 特点 |
|---|---|---|
| State | 存储应用的共享状态(唯一数据源) | 类似组件的 data,但为全局共享 |
| Getter | 对 State 进行计算派生(类似组件的 computed) | 缓存结果,依赖不变时不会重新计算 |
| Mutation | 唯一修改 State 的方式(同步操作) | 必须是同步函数,通过 commit 触发 |
| Action | 处理异步操作(如接口请求),最终通过 Mutation 修改 State | 可包含异步逻辑,通过 dispatch 触发 |
| Module | 拆分复杂状态为多个模块(每个模块包含自己的 State、Getter 等) | 避免单一状态树过于庞大,支持命名空间隔离 |
二、安装与基本配置(Vue2 + Vuex3)
Vuex 3.x 适配 Vue2,4.x 适配 Vue3,以下以 Vue2 + Vuex3 为例:
1. 安装 Vuex
npm install vuex@3 # Vue2 需指定版本 3.x
# 或
yarn add vuex@3
2. 创建 Store(核心配置)
在 src/store 目录下创建 index.js,定义 Store 实例:
// src/store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
// 1. 注册 Vuex 插件
Vue.use(Vuex)
// 2. 定义 State(共享状态)
const state = {
count: 0, // 示例:计数器状态
userInfo: null // 示例:用户信息
}
// 3. 定义 Getter(派生状态)
const getters = {
// 计算 count 的 2 倍
doubleCount: state => state.count * 2,
// 判断用户是否登录
isLogin: state => !!state.userInfo
}
// 4. 定义 Mutation(修改 State 的唯一方式)
const mutations = {
// 增加 count(同步操作)
INCREMENT(state, payload) { // 推荐使用大写常量命名
state.count += payload || 1
},
// 设置用户信息
SET_USER_INFO(state, userInfo) {
state.userInfo = userInfo
}
}
// 5. 定义 Action(处理异步,提交 Mutation)
const actions = {
// 异步增加 count(模拟接口请求延迟)
async incrementAsync({ commit }, payload) {
await new Promise(resolve => setTimeout(resolve, 1000)) // 模拟异步
commit('INCREMENT', payload) // 提交 Mutation
},
// 异步登录(模拟接口请求)
async login({ commit }, userData) {
// 模拟接口请求
const mockUser = { id: 1, name: 'Vuex' }
await new Promise(resolve => setTimeout(resolve, 500))
commit('SET_USER_INFO', mockUser) // 提交 Mutation 保存用户信息
}
}
// 6. 创建并导出 Store 实例
export default new Vuex.Store({
state,
getters,
mutations,
actions
})
3. 在 Vue 实例中注入 Store
在入口文件 main.js 中引入并注入 Store,使所有组件可访问:
// src/main.js
import Vue from 'vue'
import App from './App.vue'
import store from './store' // 引入 Store
new Vue({
store, // 注入 Store(所有组件可通过 this.$store 访问)
render: h => h(App)
}).$mount('#app')
三、核心部分使用详解
1. State:访问共享状态
组件中通过 this.$store.state 访问状态,或使用 mapState 辅助函数简化写法。
基础用法:
<template>
<div>
<p>当前计数:{{ $store.state.count }}</p>
<p>用户信息:{{ $store.state.userInfo?.name }}</p>
</div>
</template>
使用 mapState 映射到组件计算属性:
<template>
<div>
<p>当前计数:{{ count }}</p> <!-- 直接使用映射后的属性 -->
<p>用户信息:{{ userInfo?.name }}</p>
</div>
</template>
<script>
import { mapState } from 'vuex' // 导入辅助函数
export default {
computed: {
// 方式 1:数组形式(属性名与 State 一致)
...mapState(['count', 'userInfo']),
// 方式 2:对象形式(自定义属性名)
...mapState({
currentCount: state => state.count, // 函数形式
user: 'userInfo' // 字符串形式(等价于 state => state.userInfo)
})
}
}
</script>
2. Getter:派生状态计算
通过 this.$store.getters 访问,或使用 mapGetters 映射。
基础用法:
<template>
<div>
<p>计数的 2 倍:{{ $store.getters.doubleCount }}</p>
<p>是否登录:{{ $store.getters.isLogin ? '已登录' : '未登录' }}</p>
</div>
</template>
使用 mapGetters 映射:
<script>
import { mapGetters } from 'vuex'
export default {
computed: {
...mapGetters(['doubleCount', 'isLogin']),
// 自定义名称
...mapGetters({
double: 'doubleCount' // 将 doubleCount 映射为 double
})
}
}
</script>
3. Mutation:修改状态(同步)
必须通过 commit 触发,不能直接修改 State(否则无法追踪状态变更)。
基础用法:
<template>
<button @click="handleIncrement">增加计数</button>
</template>
<script>
export default {
methods: {
handleIncrement() {
// 触发 Mutation(无参数)
this.$store.commit('INCREMENT')
// 带参数(payload)
this.$store.commit('INCREMENT', 5) // 增加 5
// 对象形式(更清晰)
this.$store.commit({
type: 'INCREMENT', // Mutation 名称
payload: 10 // 参数
})
}
}
}
</script>
使用 mapMutations 映射到方法:
<script>
import { mapMutations } from 'vuex'
export default {
methods: {
...mapMutations(['INCREMENT']), // 映射为 this.INCREMENT()
...mapMutations({
add: 'INCREMENT' // 映射为 this.add(),对应 INCREMENT Mutation
}),
handleClick() {
this.INCREMENT(3) // 调用映射后的方法
this.add(5)
}
}
}
</script>
注意:Mutation 必须是同步函数(若有异步逻辑,会导致状态变更无法追踪)。
4. Action:处理异步操作
通过 dispatch 触发,内部可包含异步逻辑(如接口请求),最终通过 commit 调用 Mutation 修改状态。
基础用法:
<template>
<div>
<button @click="handleAsyncIncrement">1秒后增加计数</button>
<button @click="handleLogin">登录</button>
</div>
</template>
<script>
export default {
methods: {
handleAsyncIncrement() {
// 触发 Action(无参数)
this.$store.dispatch('incrementAsync')
// 带参数
this.$store.dispatch('incrementAsync', 5)
// 对象形式
this.$store.dispatch({
type: 'incrementAsync',
payload: 10
})
},
async handleLogin() {
// Action 支持返回 Promise,可 await
await this.$store.dispatch('login', { username: 'test' })
console.log('登录成功')
}
}
}
</script>
使用 mapActions 映射到方法:
<script>
import { mapActions } from 'vuex'
export default {
methods: {
...mapActions(['incrementAsync', 'login']), // 映射为 this.incrementAsync()
...mapActions({
asyncAdd: 'incrementAsync' // 自定义方法名
}),
handleClick() {
this.asyncAdd(3)
this.login({ username: 'vue' })
}
}
}
</script>
5. Module:拆分复杂状态
当应用状态复杂时,可将 Store 拆分为多个模块(Module),每个模块包含独立的 state、getters、mutations、actions。
示例:定义用户模块(user.js)
// src/store/modules/user.js
export default {
namespaced: true, // 开启命名空间(避免模块间命名冲突)
state: {
info: null,
token: ''
},
getters: {
isLogin: state => !!state.token
},
mutations: {
SET_TOKEN(state, token) {
state.token = token
}
},
actions: {
async login({ commit }, userData) {
// 模拟登录接口
const mockToken = 'xxx-token'
await new Promise(resolve => setTimeout(resolve, 500))
commit('SET_TOKEN', mockToken)
}
}
}
在 Store 中注册模块:
// src/store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
import user from './modules/user' // 导入用户模块
import cart from './modules/cart' // 假设有购物车模块
Vue.use(Vuex)
export default new Vuex.Store({
modules: {
user, // 注册用户模块
cart // 注册购物车模块
}
})
访问模块中的状态:
<template>
<div>
<!-- 访问模块 state -->
<p>用户 token:{{ $store.state.user.token }}</p>
<!-- 访问模块 getter(需指定模块名) -->
<p>是否登录:{{ $store.getters['user/isLogin'] }}</p>
<!-- 触发模块 mutation(需指定模块名) -->
<button @click="$store.commit('user/SET_TOKEN', 'new-token')">
设置 token
</button>
<!-- 触发模块 action(需指定模块名) -->
<button @click="$store.dispatch('user/login')">
模块登录
</button>
</div>
</template>
使用辅助函数访问命名空间模块:
<script>
import { mapState, mapActions } from 'vuex'
export default {
computed: {
...mapState('user', ['token', 'info']) // 第一个参数为模块名
},
methods: {
...mapActions('user', ['login']) // 映射 user 模块的 login Action
}
}
</script>
四、最佳实践
-
Mutation 命名常量:将 Mutation 名称定义为常量,避免拼写错误:
// src/store/mutation-types.js export const INCREMENT = 'INCREMENT' // 在 mutations 中使用 import { INCREMENT } from './mutation-types' const mutations = { [INCREMENT](state) { ... } } -
避免直接修改 State:必须通过 Mutation 修改,否则 Vuex 无法追踪状态变更。
-
Action 处理所有异步:异步逻辑(接口请求、定时器等)全部放在 Action 中,保持 Mutation 同步纯净。
-
合理拆分模块:按业务领域(用户、购物车、商品等)拆分模块,开启命名空间避免冲突。
-
状态持久化:通过
vuex-persistedstate插件将状态持久化到 localStorage,避免刷新丢失:npm install vuex-persistedstateimport createPersistedState from 'vuex-persistedstate' const store = new Vuex.Store({ // ...其他配置 plugins: [ createPersistedState({ storage: window.localStorage, // 存储到 localStorage reducer: (state) => ({ user: state.user }) // 只持久化 user 模块 }) ] })
五、适用场景
- 多个组件共享同一状态(如用户信息、全局设置)。
- 组件间状态传递复杂(如跨层级、非父子组件)。
- 需要追踪状态变更(如调试、撤销 / 重做功能)。
总结
Vuex 通过 “单一状态树” 集中管理共享状态,核心流程是:组件通过 dispatch 触发 Action 处理异步,Action 通过 commit 调用 Mutation 同步修改 State,最终 State 变更驱动组件重新渲染。合理使用 Module 拆分状态、遵循同步 / 异步分离原则,可有效管理复杂应用的状态。
1591

被折叠的 条评论
为什么被折叠?



