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>