Vuex 2 完全教程

第一章:Vuex 基础概念

1.1 Vuex 是什么?

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

练习 1.1:理解状态管理

创建一个简单的计数器应用,使用 Vuex 来管理计数器的状态。

store/index.js
import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) export default new Vuex.Store({ state: { count: 0 }, mutations: { increment(state) { state.count++ }, decrement(state) { state.count-- } }, actions: { incrementAsync({ commit }) { setTimeout(() => { commit('increment') }, 1000) } }, getters: { doubleCount: state => state.count * 2 } })
App.vue
<template> <div> <h2>计数器: {{ count }}</h2> <h3>双倍计数: {{ doubleCount }}</h3> <button @click="increment">增加</button> <button @click="decrement">减少</button> <button @click="incrementAsync">异步增加</button> </div> </template> <script> import { mapState, mapGetters, mapMutations, mapActions } from 'vuex' export default { computed: { ...mapState(['count']), ...mapGetters(['doubleCount']) }, methods: { ...mapMutations(['increment', 'decrement']), ...mapActions(['incrementAsync']) } } </script>

第二章:核心概念

2.1 State

State 是 Vuex 中的单一状态树,存储着应用的所有状态。

练习 2.1:状态管理

创建一个待办事项列表,使用 Vuex 管理待办事项的状态。

store/modules/todos.js
export default { state: { todos: [ { id: 1, text: '学习 Vuex', done: true }, { id: 2, text: '完成项目', done: false } ] }, mutations: { ADD_TODO(state, todo) { state.todos.push(todo) }, TOGGLE_TODO(state, id) { const todo = state.todos.find(todo => todo.id === id) if (todo) { todo.done = !todo.done } } }, actions: { addTodo({ commit }, text) { commit('ADD_TODO', { id: Date.now(), text, done: false }) } }, getters: { doneTodos: state => state.todos.filter(todo => todo.done), activeTodos: state => state.todos.filter(todo => !todo.done) } }

2.2 Getters

Getters 用于从 store 中的 state 中派生出一些状态。

练习 2.2:计算属性

在待办事项应用中,添加一个 getter 来计算已完成事项的数量。

store/modules/todos.js (getters 部分)
getters: { doneTodos: state => state.todos.filter(todo => todo.done), activeTodos: state => state.todos.filter(todo => !todo.done), doneTodosCount: (state, getters) => getters.doneTodos.length, getTodoById: state => id => state.todos.find(todo => todo.id === id) }

2.3 Mutations

Mutations 是更改 Vuex 的 store 中状态的唯一方法。

练习 2.3:状态修改

实现待办事项的添加、删除和标记完成功能。

store/modules/todos.js (mutations 部分)
mutations: { ADD_TODO(state, todo) { state.todos.push(todo) }, DELETE_TODO(state, id) { state.todos = state.todos.filter(todo => todo.id !== id) }, TOGGLE_TODO(state, id) { const todo = state.todos.find(todo => todo.id === id) if (todo) { todo.done = !todo.done } }, UPDATE_TODO(state, { id, text }) { const todo = state.todos.find(todo => todo.id === id) if (todo) { todo.text = text } } }

2.4 Actions

Actions 用于处理异步操作和提交 mutations。

练习 2.4:异步操作

实现从服务器获取待办事项列表的功能。

store/modules/todos.js (actions 部分)
actions: { fetchTodos({ commit }) { return new Promise((resolve, reject) => { fetch('/api/todos') .then(response => response.json()) .then(todos => { commit('SET_TODOS', todos) resolve(todos) }) .catch(error => { console.error('获取待办事项失败:', error) reject(error) }) }) }, addTodo({ commit }, text) { return new Promise((resolve, reject) => { fetch('/api/todos', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ text }) }) .then(response => response.json()) .then(todo => { commit('ADD_TODO', todo) resolve(todo) }) .catch(error => { console.error('添加待办事项失败:', error) reject(error) }) }) } }

2.5 Modules

Modules 允许我们将 store 分割成模块,每个模块拥有自己的 state、mutations、actions、getters。

练习 2.5:模块化

将待办事项应用拆分为用户模块和待办事项模块。

store/index.js
import Vue from 'vue' import Vuex from 'vuex' import todos from './modules/todos' import user from './modules/user' Vue.use(Vuex) export default new Vuex.Store({ modules: { todos, user } })
store/modules/user.js
export default { namespaced: true, state: { user: null, token: null }, mutations: { SET_USER(state, user) { state.user = user }, SET_TOKEN(state, token) { state.token = token } }, actions: { login({ commit }, credentials) { return new Promise((resolve, reject) => { fetch('/api/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(credentials) }) .then(response => response.json()) .then(({ user, token }) => { commit('SET_USER', user) commit('SET_TOKEN', token) resolve({ user, token }) }) .catch(error => { console.error('登录失败:', error) reject(error) }) }) } }, getters: { isAuthenticated: state => !!state.token, currentUser: state => state.user } }

第三章:综合案例

3.1 电商购物车案例

通过一个完整的电商购物车案例,综合运用 Vuex 的所有核心概念。

练习 3.1:购物车功能实现

实现以下功能:

store/modules/products.js
export default { namespaced: true, state: { products: [ { id: 1, name: '商品1', price: 100, stock: 10 }, { id: 2, name: '商品2', price: 200, stock: 5 } ] }, mutations: { UPDATE_STOCK(state, { id, quantity }) { const product = state.products.find(p => p.id === id) if (product) { product.stock -= quantity } } } }
store/modules/cart.js
export default { namespaced: true, state: { items: [] }, mutations: { ADD_TO_CART(state, { product, quantity }) { const existingItem = state.items.find(item => item.id === product.id) if (existingItem) { existingItem.quantity += quantity } else { state.items.push({ id: product.id, name: product.name, price: product.price, quantity }) } }, REMOVE_FROM_CART(state, id) { state.items = state.items.filter(item => item.id !== id) }, UPDATE_QUANTITY(state, { id, quantity }) { const item = state.items.find(item => item.id === id) if (item) { item.quantity = quantity } } }, actions: { addToCart({ commit, rootState }, { productId, quantity }) { const product = rootState.products.products.find(p => p.id === productId) if (product && product.stock >= quantity) { commit('ADD_TO_CART', { product, quantity }) commit('products/UPDATE_STOCK', { id: productId, quantity }, { root: true }) } } }, getters: { cartItems: state => state.items, cartTotal: state => state.items.reduce((total, item) => total + item.price * item.quantity, 0), cartItemCount: state => state.items.reduce((count, item) => count + item.quantity, 0) } }

第四章:最佳实践

4.1 项目结构

介绍如何组织大型 Vuex 项目的文件结构。

推荐的项目结构
src/ ├── store/ │ ├── index.js # 组装模块并导出 store │ ├── actions.js # 根级别的 action │ ├── mutations.js # 根级别的 mutation │ └── modules/ │ ├── cart.js # 购物车模块 │ ├── products.js # 产品模块 │ └── user.js # 用户模块

4.2 命名规范

Vuex 相关的命名规范和约定。

命名规范示例
// 模块名使用小写 export default { namespaced: true, state: { // 状态名使用小写 items: [] }, mutations: { // mutation 名使用大写,用下划线分隔 ADD_ITEM(state, item) { state.items.push(item) } }, actions: { // action 名使用驼峰命名 fetchItems({ commit }) { // ... } }, getters: { // getter 名使用驼峰命名 getItemById: state => id => { return state.items.find(item => item.id === id) } } }

4.3 性能优化

如何优化 Vuex 应用的性能。

性能优化示例
// 1. 使用 mapState 和 mapGetters 优化组件中的计算属性 import { mapState, mapGetters } from 'vuex' export default { computed: { ...mapState('cart', ['items']), ...mapGetters('cart', ['cartTotal']) } } // 2. 使用严格模式进行开发调试 export default new Vuex.Store({ strict: process.env.NODE_ENV !== 'production' }) // 3. 使用模块化避免状态树过大 export default new Vuex.Store({ modules: { cart, products, user } })

第五章:常见问题解答

5.1 常见错误

列举并解决使用 Vuex 时常见的错误。

常见错误及解决方案
// 错误1:直接修改 state // ❌ 错误做法 state.count = 10 // ✅ 正确做法 mutations: { SET_COUNT(state, value) { state.count = value } } // 错误2:在 action 中直接修改 state // ❌ 错误做法 actions: { updateCount({ state }, value) { state.count = value } } // ✅ 正确做法 actions: { updateCount({ commit }, value) { commit('SET_COUNT', value) } } // 错误3:忘记使用命名空间 // ❌ 错误做法 commit('ADD_ITEM', item) // ✅ 正确做法 commit('cart/ADD_ITEM', item, { root: true })

5.2 调试技巧

介绍 Vuex 的调试工具和技巧。

调试技巧示例
// 1. 使用 Vue Devtools 进行调试 // 在 main.js 中配置 import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) const store = new Vuex.Store({ // ... devtools: true }) // 2. 使用 logger 插件记录状态变化 import createLogger from 'vuex/dist/logger' const store = new Vuex.Store({ // ... plugins: process.env.NODE_ENV !== 'production' ? [createLogger()] : [] }) // 3. 在组件中监听状态变化 export default { watch: { '$store.state.cart.items': { handler(newItems) { console.log('购物车内容变化:', newItems) }, deep: true } } }

学习建议

建议按照章节顺序学习,每完成一个练习后再进行下一个。遇到问题时,可以查看对应的常见问题解答部分。