##一、为什么需要 Vuex??
在 Vue2 项目开发中,如果你遇到以下问题,就该考虑使用 Vuex 了:
Vuex 把组件的共享状态抽取出来,以一个全局单例模式管理,让组件树构成一个巨大的“视图”,不管在哪个组件,都能获取状态或触发行为,同时保证所有状态的变更都遵循统一的规则,使代码可预测。
二、Vuex 的核心概念(Vue2 版本)
Vuex 的核心由 State、Getter、Mutation、Action、Module 五部分组成,我们先理清每个部分的作用:
1. State:唯一数据源
State 是 Vuex 存储状态(数据)的地方,相当于组件中的 data,但它是全局的,所有组件都能访问。
核心特点:
2. Getter:计算属性
Getter 相当于 Vue 组件中的 computed,用于对 State 中的数据进行二次处理(如过滤、计算),结果会被缓存,只有依赖的数据变化时才会重新计算。
3. Mutation:唯一修改状态的方式
Mutation 是修改 State 的唯一入口,必须是同步函数(异步操作会导致状态变更无法追踪)。每个 Mutation 有一个字符串类型的 type 和一个 handler 函数,handler 函数接收 state 作为第一个参数,可接收第二个参数(载荷 payload)。
4. Action:处理异步操作
Action 用于处理异步逻辑(如接口请求),不能直接修改 State,必须通过提交 Mutation 来修改状态。Action 可以包含任意异步操作,支持提交多个 Mutation。
5. Module:模块化管理
当项目规模较大时,单一 State 会变得臃肿,Module 允许将 Store 分割成多个模块,每个模块拥有自己的 State、Getter、Mutation、Action,便于维护。
三、Vue2 中 Vuex 的使用步骤
1. 环境准备
首先确保项目是 Vue2 环境,安装对应版本的 Vuex(Vue2 对应 Vuex 3.x,Vue3 对应 Vuex 4.x):
1 2 3 4
| npm install vuex@3 --save
yarn add vuex@3
|
2. 创建 Store 实例
在项目 src 目录下新建 store 文件夹,创建 index.js:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
| import Vue from 'vue' import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({ state: { userInfo: { name: '', token: '' }, cartList: [] },
getters: { cartTotalCount: state => { return state.cartList.reduce((total, item) => total + item.quantity, 0) }, selectedCartItems: state => { return state.cartList.filter(item => item.checked) } },
mutations: { SET_USER_INFO(state, payload) { state.userInfo = { ...state.userInfo, ...payload } }, ADD_TO_CART(state, goods) { const existItem = state.cartList.find(item => item.id === goods.id) if (existItem) { existItem.quantity += 1 } else { state.cartList.push({ ...goods, quantity: 1, checked: true }) } }, CLEAR_CART(state) { state.cartList = [] } },
actions: { async fetchUserInfo({ commit }, userId) { try { const res = await new Promise(resolve => { setTimeout(() => { resolve({ name: '张三', token: 'abc123456' }) }, 1000) }) commit('SET_USER_INFO', res) return res } catch (error) { console.error('获取用户信息失败:', error) throw error } }, async clearCart({ commit }) { try { await new Promise(resolve => setTimeout(resolve, 500)) commit('CLEAR_CART') } catch (error) { console.error('清空购物车失败:', error) } } } })
export default store
|
3. 在 Vue 项目中挂载 Store
修改 src/main.js,将 Store 挂载到 Vue 实例:
1 2 3 4 5 6 7 8 9 10
| import Vue from 'vue' import App from './App.vue' import store from './store'
new Vue({ el: '#app', store, render: h => h(App) })
|
4. 组件中使用 Vuex
(1)获取 State 数据
有两种方式:直接通过 this.$store.state 访问,或使用 mapState 辅助函数(推荐,简化代码)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| <template> <div class="user-info"> <p>用户名:{{ userInfo.name }}</p> <p>购物车商品数:{{ cartList.length }}</p> </div> </template>
<script> import { mapState } from 'vuex'
export default { computed: { // 方式1:直接访问 // userInfo() { // return this.$store.state.userInfo // }, // cartList() { // return this.$store.state.cartList // }
// 方式2:mapState 辅助函数(推荐) ...mapState(['userInfo', 'cartList']) } } </script>
|
(2)使用 Getter
同理,可通过 this.$store.getters 或 mapGetters 辅助函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <template> <div class="cart"> <p>购物车总数:{{ cartTotalCount }}</p> <p>已选中商品:{{ selectedCartItems.length }} 件</p> </div> </template>
<script> import { mapGetters } from 'vuex'
export default { computed: { ...mapGetters(['cartTotalCount', 'selectedCartItems']) } } </script>
|
(3)提交 Mutation 修改状态
Mutation 必须通过 commit 触发,支持 this.$store.commit 或 mapMutations 辅助函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <template> <button @click="addGoods">添加商品到购物车</button> </template>
<script> import { mapMutations } from 'vuex'
export default { methods: { addGoods() { // 方式1:直接 commit // this.$store.commit('ADD_TO_CART', { id: 1, name: 'Vue实战教程', price: 99 })
// 方式2:mapMutations 辅助函数 this.ADD_TO_CART({ id: 1, name: 'Vue实战教程', price: 99 }) }, ...mapMutations(['ADD_TO_CART']) } } </script>
|
(4)触发 Action 处理异步
Action 通过 dispatch 触发,支持 this.$store.dispatch 或 mapActions 辅助函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| <template> <div> <button @click="getUserInfo">获取用户信息</button> <button @click="clearCart">清空购物车</button> </div> </template>
<script> import { mapActions } from 'vuex'
export default { methods: { async getUserInfo() { // 方式1:直接 dispatch // await this.$store.dispatch('fetchUserInfo', 1001)
// 方式2:mapActions 辅助函数 await this.fetchUserInfo(1001) console.log('用户信息已获取:', this.userInfo) }, async clearCart() { await this.clearCart() console.log('购物车已清空') }, ...mapActions(['fetchUserInfo', 'clearCart']) }, computed: { userInfo() { return this.$store.state.userInfo } } } </script>
|
5. 模块化(Module)实战
当项目复杂时,拆分 Store 为多个模块,例如拆分 user 和 cart 模块:
步 骤1:创建模块文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| export default { namespaced: true, state: { userInfo: { name: '', token: '' } }, getters: { isLogin: state => !!state.userInfo.token }, mutations: { SET_USER_INFO(state, payload) { state.userInfo = { ...state.userInfo, ...payload } } }, actions: { async fetchUserInfo({ commit }, userId) { const res = await new Promise(resolve => { setTimeout(() => resolve({ name: '张三', token: 'abc123' }), 1000) }) commit('SET_USER_INFO', res) } } }
export default { namespaced: true, state: { cartList: [] }, getters: { cartTotalCount: state => state.cartList.reduce((t, i) => t + i.quantity, 0) }, mutations: { ADD_TO_CART(state, goods) { const exist = state.cartList.find(i => i.id === goods.id) exist ? exist.quantity++ : state.cartList.push({ ...goods, quantity: 1 }) } }, actions: { async clearCart({ commit }) { await new Promise(resolve => setTimeout(resolve, 500)) commit('CLEAR_CART') } } }
|
步 骤2:整合模块到 Store
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| 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 } })
|
步 骤3:组件中使用命名空间模块
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| <template> <div> <p>用户名:{{ userInfo.name }}</p> <p>购物车总数:{{ cartTotalCount }}</p> </div> </template>
<script> import { mapState, mapGetters, mapActions } from 'vuex'
export default { computed: { // 方式1:带命名空间的 mapState ...mapState('user', ['userInfo']), // 方式2:直接访问 // userInfo() { // return this.$store.state.user.userInfo // }, ...mapGetters('cart', ['cartTotalCount']) }, methods: { ...mapActions('user', ['fetchUserInfo']), ...mapActions('cart', ['clearCart']), async init() { await this.fetchUserInfo(1001) } }, created() { this.init() } } </script>
|
四、Vuex 使用
1. 严格模式
开启严格模式后,任何不是通过 Mutation 修改 State 的操作都会抛出错误,便于调试(生产环境建议关闭):
1 2 3 4
| const store = new Vuex.Store({ strict: process.env.NODE_ENV !== 'production', })
|
2. 避免直接修改 State
永远不要在组件中直接修改 this.$store.state.xxx,必须通过 Mutation 或 Action 提交 Mutation,否则状态变更无法追踪。
3. 合理拆分模块
按业务域拆分模块(如 user、cart、order),每个模块独立维护;
开启 namespaced: true,避免命名冲突;
复杂模块可进一步拆分(如 cart 下拆分子模块 cartItem、cartPrice)。
4. 异步逻辑统一放在 Action
所有异步操作(接口请求、定时器、Promise)都放在 Action 中,Mutation 只做同步的状态修改,保证状态变更可追踪。
5. 数据持久化
Vuex 状态刷新后会丢失,可结合 vuex-persistedstate 实现本地存储持久化:
1
| npm install vuex-persistedstate@3 --save
|
1 2 3 4 5 6 7 8 9 10 11 12
| import createPersistedState from 'vuex-persistedstate'
const store = new Vuex.Store({ plugins: [ createPersistedState({ key: 'vue2-vuex-demo', paths: ['user.userInfo', 'cart.cartList'] }) ] })
|
6. 避免过度使用 Vuex
并非所有数据都要放入 Vuex,只有跨组件共享、全局使用的状态(如用户信息、购物车、全局配置)才适合,组件内部的私有状态(如表单临时值)直接用组件 data 即可。
五、常见问题与解决方案
1. 模块中获取根状态/根 Getter
在模块的 Getter/Action 中,可通过第二个参数获取根状态:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| getters: { cartTotalPrice: (state, getters, rootState, rootGetters) => { return state.cartList.reduce((t, i) => t + i.price * i.quantity, 0) } }
actions: { async updateCart({ commit, rootState }) { const token = rootState.user.userInfo.token await api.updateCart(token, state.cartList) } }
|
2. 辅助函数简化命名空间写法
可通过绑定命名空间的方式简化代码:
1 2 3 4 5 6 7 8 9 10 11
| import { createNamespacedHelpers } from 'vuex' const { mapState, mapActions } = createNamespacedHelpers('cart')
export default { computed: { ...mapState(['cartList']) }, methods: { ...mapActions(['addToCart']) } }
|
六、总结
Vuex 是 Vue2 中解决跨组件共享状态的核心方案,核心是“单向数据流”:View → Dispatch Action → Commit Mutation → Modify State → Update View;
Vuex 3.x 适配 Vue2,核心概念包括 State(状态)、Getter(计算)、Mutation(同步修改)、Action(异步)、Module(模块化);
“异步逻辑放 Action,同步修改放 Mutation”,复杂项目拆分模块并开启命名空间,结合 vuex-persistedstate 实现状态持久化;
避免过度使用 Vuex,仅管理全局共享状态,组件私有状态仍用 data 维护。