Vuex 超详细使用教程(从入门到精通)
一、Vuex 是什么?
Vuex 是专门为 Vue.js 设计的状态管理库,它采用集中式存储管理应用的所有组件的状态。简单来说,Vuex 就是一个"全局变量仓库",所有组件都可以从这里获取数据或修改数据。
二、为什么要用 Vuex?
当你的 Vue 应用变得复杂时,组件之间的数据共享和通信会变得困难。Vuex 解决了以下问题:
多个组件共享同一状态
不同组件需要变更同一状态
组件深层嵌套时的数据传递
三、安装与基础配置
1. 安装 Vuex
npm install vuex --save
# 或
yarn add vuex
2. 创建 Store
在项目中创建 store/index.js
文件:
import Vue from 'vue'
import Vuex from 'vuex'Vue.use(Vuex) // 告诉 Vue 使用 Vuex// 创建 Vuex Store 实例
const store = new Vuex.Store({// 在这里配置 Vuex
})export default store
3. 在 Vue 实例中引入
在 main.js
中:
import Vue from 'vue'
import App from './App.vue'
import store from './store' // 导入我们创建的 storenew Vue({store, // 注入 storerender: h => h(App)
}).$mount('#app')
四、核心概念详解
1. State - 状态仓库
State 就是存储数据的地方,相当于组件中的 data。
const store = new Vuex.Store({state: {count: 0,user: {name: '张三',age: 25},todos: [{ id: 1, text: '学习Vuex', done: true },{ id: 2, text: '写项目', done: false }]}
})
在组件中访问 State
方法1:直接通过 this.$store.state
访问
computed: {count() {return this.$store.state.count},userName() {return this.$store.state.user.name}
}
方法2:使用 mapState
辅助函数
import { mapState } from 'vuex'export default {computed: {// 数组形式 - 同名映射...mapState(['count', 'user']),// 对象形式 - 可自定义名称...mapState({myCount: 'count',currentUser: state => state.user})}
}
2. Getters - 计算属性
Getters 相当于 Store 的计算属性,用于从 state 中派生出一些状态。
const store = new Vuex.Store({state: {todos: [{ id: 1, text: '学习Vuex', done: true },{ id: 2, text: '写项目', done: false }]},getters: {// 获取已完成的任务doneTodos: state => {return state.todos.filter(todo => todo.done)},// 获取未完成的任务数量undoneTodosCount: (state, getters) => {return state.todos.length - getters.doneTodos.length},// 通过ID获取特定任务getTodoById: state => id => {return state.todos.find(todo => todo.id === id)}}
})
在组件中使用 Getters
方法1:直接通过 this.$store.getters
访问
computed: {doneTodos() {return this.$store.getters.doneTodos},todo() {return this.$store.getters.getTodoById(2)}
}
方法2:使用 mapGetters
辅助函数
import { mapGetters } from 'vuex'export default {computed: {// 数组形式 - 同名映射...mapGetters(['doneTodos', 'undoneTodosCount']),// 对象形式 - 可自定义名称...mapGetters({doneList: 'doneTodos',todo: 'getTodoById'})},methods: {getTodo(id) {return this.todo(id) // 使用带参数的getter}}
}
3. Mutations - 修改状态
Mutations 是修改 State 的唯一途径,每个 mutation 都有一个字符串的 事件类型 (type) 和一个 回调函数 (handler)。
const store = new Vuex.Store({state: {count: 1},mutations: {// 基本形式increment(state) {state.count++},// 带参数的形式incrementBy(state, payload) {state.count += payload.amount},// 对象风格的提交decrement(state, { amount }) {state.count -= amount}}
})
提交 Mutations
方法1:直接提交
methods: {increment() {this.$store.commit('increment')},addFive() {// 提交带参数的mutationthis.$store.commit('incrementBy', { amount: 5 })// 对象风格提交this.$store.commit({type: 'incrementBy',amount: 5})}
}
方法2:使用 mapMutations
辅助函数
methods: {increment() {this.$store.commit('increment')},addFive() {// 提交带参数的mutationthis.$store.commit('incrementBy', { amount: 5 })// 对象风格提交this.$store.commit({type: 'incrementBy',amount: 5})}
}
import { mapMutations } from 'vuex'export default {methods: {// 数组形式 - 同名映射...mapMutations(['increment', 'incrementBy']),// 对象形式 - 可自定义名称...mapMutations({add: 'increment',addAmount: 'incrementBy'}),// 使用方法addFive() {this.addAmount({ amount: 5 })}}
}
重要规则:
Mutation 必须是同步函数
不要在 mutation 中执行异步操作
建议使用常量替代 Mutation 事件类型(大型项目)
4. Actions - 处理异步操作
Actions 类似于 mutations,不同在于:
Actions 提交的是 mutations,而不是直接变更状态
Actions 可以包含任意异步操作
const store = new Vuex.Store({state: {count: 0},mutations: {increment(state) {state.count++}},actions: {// 基本形式incrementAsync({ commit }) {setTimeout(() => {commit('increment')}, 1000)},// 带参数的形式incrementByAsync({ commit }, payload) {return new Promise((resolve) => {setTimeout(() => {commit('increment')resolve()}, payload.delay)})},// 组合多个actionactionA({ commit }) {return new Promise((resolve) => {setTimeout(() => {commit('someMutation')resolve()}, 1000)})},actionB({ dispatch, commit }) {return dispatch('actionA').then(() => {commit('someOtherMutation')})}}
})
分发 Actions
方法1:直接分发
methods: {increment() {this.$store.dispatch('incrementAsync')},addLater() {// 带参数this.$store.dispatch('incrementByAsync', { delay: 2000 })// 对象风格this.$store.dispatch({type: 'incrementByAsync',delay: 2000})// 处理Promisethis.$store.dispatch('actionB').then(() => {console.log('Action completed')})}
}
方法2:使用 mapActions
辅助函数
import { mapActions } from 'vuex'export default {methods: {// 数组形式 - 同名映射...mapActions(['incrementAsync', 'incrementByAsync']),// 对象形式 - 可自定义名称...mapActions({add: 'incrementAsync',addLater: 'incrementByAsync'}),// 使用方法addFiveLater() {this.addLater({ delay: 5000 }).then(() => console.log('Done!'))}}
}
5. Modules - 模块化
当应用变得复杂时,Store 对象可能变得臃肿。Vuex 允许我们将 store 分割成模块(module)。
const moduleA = {namespaced: true, // 启用命名空间state: { count: 0 },mutations: {increment(state) {state.count++}},getters: {doubleCount(state) {return state.count * 2}}
}const moduleB = {namespaced: true,state: { list: [] },actions: {fetchList({ commit }) {// 获取数据...}}
}const store = new Vuex.Store({modules: {a: moduleA,b: moduleB}
})
访问模块内容
// 访问模块 state
this.$store.state.a.count // -> moduleA 的 state
this.$store.state.b.list // -> moduleB 的 state// 提交模块 mutation
this.$store.commit('a/increment')// 分发模块 action
this.$store.dispatch('b/fetchList')// 使用模块 getter
this.$store.getters['a/doubleCount']
使用辅助函数
import { mapState, mapGetters, mapMutations, mapActions } from 'vuex'export default {computed: {// 模块 state...mapState('a', ['count']),...mapState('b', ['list']),// 模块 getters...mapGetters('a', ['doubleCount'])},methods: {// 模块 mutations...mapMutations('a', ['increment']),// 模块 actions...mapActions('b', ['fetchList'])}
}
模块局部状态
在模块内部的 mutation 和 getter 中,接收的第一个参数是模块的局部状态对象:
const moduleA = {state: { count: 0 },mutations: {increment(state) {// state 是模块的局部状态state.count++}},getters: {doubleCount(state) {return state.count * 2},// 可以访问根节点状态sumWithRootCount(state, getters, rootState) {return state.count + rootState.count}},actions: {// 在action中可以通过 context.rootState 访问根状态incrementIfOdd({ state, commit, rootState }) {if ((state.count + rootState.count) % 2 === 1) {commit('increment')}}}
}
五、实际项目应用示例
1. 用户认证模块示例
// store/modules/auth.js
const state = {user: null,token: localStorage.getItem('token') || '',status: '' // 'loading', 'success', 'error'
}const getters = {isAuthenticated: state => !!state.token,authStatus: state => state.status,currentUser: state => state.user
}const mutations = {AUTH_REQUEST(state) {state.status = 'loading'},AUTH_SUCCESS(state, { token, user }) {state.status = 'success'state.token = tokenstate.user = userlocalStorage.setItem('token', token)},AUTH_ERROR(state) {state.status = 'error'localStorage.removeItem('token')},LOGOUT(state) {state.user = nullstate.token = ''localStorage.removeItem('token')}
}const actions = {login({ commit }, userData) {return new Promise((resolve, reject) => {commit('AUTH_REQUEST')axios.post('/api/auth/login', userData).then(res => {const { token, user } = res.datacommit('AUTH_SUCCESS', { token, user })resolve(res)}).catch(err => {commit('AUTH_ERROR')localStorage.removeItem('token')reject(err)})})},logout({ commit }) {return new Promise(resolve => {commit('LOGOUT')resolve()})}
}export default {namespaced: true,state,getters,mutations,actions
}
2. 在组件中使用
写文章-CSDN创作中心
<template><div><div v-if="!isAuthenticated"><button @click="login">登录</button></div><div v-else>欢迎, {{ currentUser.name }}!<button @click="logout">退出</button></div></div>
</template><script>
import { mapGetters, mapActions } from 'vuex'export default {computed: {...mapGetters('auth', ['isAuthenticated', 'currentUser'])},methods: {...mapActions('auth', ['login', 'logout']),login() {const userData = { username: 'test', password: '123456' }this.login(userData).then(() => {this.$router.push('/dashboard')}).catch(error => {console.error('登录失败:', error)})}}
}</script>