Vue2 与 Vuex 状态管理实战指南
一、初识 Vuex
1. 为什么需要 Vuex?
在多组件共享状态的场景中(如用户登录信息、购物车数据),传统父子组件通信方式存在以下问题:
- ** prop drilling(属性钻孔)**:多层组件间传递数据需逐层传递
- 事件总线复杂性:事件订阅/发布模式难以维护
- 全局变量风险:使用全局变量会导致状态修改不可控
2. 核心概念
graph TD
A[Vue 实例] --> B(Vuex Store)
B --> C[State]
B --> D[Getters]
B --> E[Mutations]
B --> F[Actions]
B --> G[Modules]
二、快速上手
1. 安装与配置
bash
# 安装指定版本(Vue2 必须使用 Vuex 3)
npm install [email protected]
javascript
// store/index.js
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
export default new Vuex.Store({
state: {
count: 0,
user: null
},
mutations: {
INCREASE(state) {
state.count++;
},
SET_USER(state, payload) {
state.user = payload;
}
},
actions: {
asyncIncrease({ commit }) {
setTimeout(() => {
commit('INCREASE');
}, 1000);
}
}
});
javascript
// main.js
import Vue from 'vue';
import App from './App.vue';
import store from './store';
new Vue({
el: '#app',
store, // 注入到 Vue 实例
render: h => h(App)
});
2. 第一个示例:计数器
vue
<!-- Counter.vue -->
<template>
<div>
<p>{{ count }}</p >
<button @click="increment">+1</button>
<button @click="asyncIncrement">异步+1</button>
</div>
</template>
<script>
import { mapState, mapActions } from 'vuex';
export default {
computed: {
...mapState(['count']) // 映射为计算属性
},
methods: {
...mapActions(['asyncIncrease']),
increment() {
this.$store.commit('INCREASE'); // 直接提交 mutation
}
}
};
</script>
三、核心功能详解
1. State 存储体系
javascript
// store/modules/cart.js
const state = () => ({
items: [], // 购物车商品列表
total: 0 // 总金额
});
2. Mutations 同步修改
javascript
// store/modules/cart.js
mutations: {
ADD_ITEM(state, item) {
state.items.push(item);
state.total += item.price;
},
REMOVE_ITEM(state, itemId) {
const index = state.items.findIndex(i => i.id === itemId);
if (index !== -1) {
state.total -= state.items[index].price;
state.items.splice(index, 1);
}
}
}
3. Actions 异步处理
javascript
// store/modules/cart.js
actions: {
fetchItems({ commit }) {
// 模拟 API 请求
setTimeout(() => {
const items = [
{ id: 1, name: 'iPhone', price: 5000 },
{ id: 2, name: 'MacBook', price: 15000 }
];
items.forEach(item => commit('ADD_ITEM', item));
}, 500);
}
}
4. Getters 数据加工
javascript
// store/modules/cart.js
getters: {
itemCount(state) {
return state.items.length;
},
discountTotal(state) {
return state.total * 0.9; // 模拟九折优惠
}
}
四、模块化架构实践
场景:电商系统状态管理
javascript
// store/index.js
import Vue from 'vue';
import Vuex from 'vuex';
import cart from './modules/cart';
import user from './modules/user';
Vue.use(Vuex);
export default new Vuex.Store({
modules: {
cart, // 购物车模块
user // 用户模块
}
});
javascript
// store/modules/user.js
const state = () => ({
isLoggedIn: false,
info: null
});
const mutations = {
LOGIN(state, userInfo) {
state.isLoggedIn = true;
state.info = userInfo;
}
};
const actions = {
login({ commit }, username) {
// 模拟登录验证
setTimeout(() => {
const user = { name: username, age: 20 };
commit('LOGIN', user);
}, 1000);
}
};
export default {
state,
mutations,
actions
};
五、最佳实践
1. 严格模式开发
javascript
// store/index.js
export default new Vuex.Store({
strict: true, // 禁止直接修改 state
modules: {
cart,
user
}
});
2. 辅助函数完全指南
vue
<!-- 使用 map 系列辅助函数 -->
<script>
import { mapState, mapGetters, mapActions, mapMutations } from 'vuex';
export default {
computed: {
...mapState({
cartItems: state => state.cart.items
}),
...mapGetters({
totalPrice: 'cart/discountTotal'
})
},
methods: {
...mapActions('cart', ['fetchItems']),
...mapMutations('user', ['LOGIN'])
}
};
</script>
3. 命名空间管理
javascript
// 访问带命名空间的模块
this.$store.dispatch('cart/fetchItems');
this.$store.commit('user/LOGIN', userData);
六、完整购物车示例
目录结构
css
src/
├── store/
│ ├── index.js
│ ├── modules/
│ │ ├── cart.js
│ │ └── user.js
├── components/
│ ├── CartList.vue
│ ├── UserProfile.vue
│ └── Main.vue
CartList.vue
vue
<template>
<div>
<h2>购物车</h2>
<p>总价:{{ totalPrice | currency }}</p >
<ul>
<li v-for="item in cartItems" :key="item.id">
{{ item.name }} - ¥{{ item.price }}
</li>
</ul>
<button @click="removeItem(1)">删除 iPhone</button>
</div>
</template>
<script>
import { mapState, mapGetters, mapMutations } from 'vuex';
export default {
computed: {
...mapState('cart', ['items']),
...mapGetters('cart', ['discountTotal'])
},
methods: {
...mapMutations('cart', ['REMOVE_ITEM']),
removeItem(id) {
this.REMOVE_ITEM(id);
}
}
};
</script>
七、总结与建议
1. 适用场景
- 多组件共享状态(如用户认证、主题设置)
- 复杂数据流管理(如表单向导、多步流程)
- 需要状态持久化的场景
2. 注意事项
- 避免过度集中:简单状态可保留在组件内
- 严格模式警告:防止意外修改状态
- 性能优化:使用模块化减少watcher数量
3. 扩展学习
- 结合 Vue Devtools 调试状态变化
- 学习 Vuex Persistedstate 插件实现状态持久化
- 对比 Redux 理解不同状态管理方案差异
通过本文的购物车案例,你已经掌握从基础状态管理到模块化架构的完整流程。在实际项目中,建议:
- 按业务领域划分模块
- 使用命名空间避免命名冲突
- 封装常用逻辑到 Vuex 插件
- 配合 TypeScript 增强类型安全(Vue2 需使用 Vuex 3 + TS)