[TOC] #### 1. Vuex 介紹 --- Vuex 官方文檔: [https://vuex.vuejs.org/zh](https://vuex.vuejs.org/zh) Vuex 當(dāng)前最新版為 Vuex 4.x 【當(dāng)前時間 2022-10】 Vue 3 使用 Vuex 4,而 Vue 2 使用 Vuex 3,本文記錄的是 Vuex3 的使用總結(jié),[Vuex 3.x 文檔](https://v3.vuex.vuejs.org/zh) **一、組件之間共享數(shù)據(jù)的方式:** 父向子傳值: v-bind 屬性綁定,子向父傳值: v-on 事件綁定 **二、那么頁面之間如何共享數(shù)據(jù)呢 ?這就需要使用到 vuex 了** vuex 可以實現(xiàn)多個組件中共享狀態(tài)(數(shù)據(jù)) 官方解釋: Vuex 是一個專為 Vue.js 應(yīng)用程序開發(fā)的狀態(tài)管理模式 換句話說,vuex 是實現(xiàn)組件全局狀態(tài)(數(shù)據(jù))管理的一種機制,可以方便的實現(xiàn)組件之間數(shù)據(jù)的共享 **三、Vuex 術(shù)語** 在 vuex 中,`狀態(tài)`指的是共享的數(shù)據(jù),也就是 vuex 的 state 的值 **四、使用 vuex 統(tǒng)一管理狀態(tài)的好處** a. 高效的實現(xiàn)數(shù)據(jù)共享,提高開發(fā)效率 b. 集中管理共享的數(shù)據(jù),易于開發(fā)和后期維護(hù) c. 存儲在 vuex 中的數(shù)據(jù)都是響應(yīng)式的,能夠?qū)崟r保持?jǐn)?shù)據(jù)與頁面的同步 #### 2. Vuex 安裝 --- 將 vuex 安裝為 運行時依賴【本文章默認(rèn)項目環(huán)境為 vue 腳手架】 vue 腳手架需要安裝該依賴,而 uniapp 項目已經(jīng)內(nèi)置 vuex,無需安裝即可直接使用 ``` npm install vuex --save ``` 創(chuàng)建文件: **src/store/index.js**, 文件內(nèi)容如下: ```javascript import Vue from 'vue'; import Vuex from 'vuex'; // 1. 安裝插件 Vue.use(Vuex) // 2. 創(chuàng)建對象 const store = new Vuex.Store({ }) // 3. 導(dǎo)入 store 對象 export default store ``` 在 main.js 中導(dǎo)入 store 對象,并將 store 對象掛載到 vue 實例上 ```javascript import store from './store' new Vue({ el: '#app', store, render: h => h(App) }) ``` #### 3. Vue.js devtools 插件 --- 多個界面修改 vuex 狀態(tài)時,這個工具會對狀態(tài)進(jìn)行跟蹤,當(dāng)出現(xiàn)問題時,可以更好的調(diào)試錯誤 ![](https://img.itqaq.com/art/content/7297312b90f31a41faab9754bf48fb5a.png) #### 4. state 數(shù)據(jù)的訪問方式 --- **方法一:通過 vue 實例訪問** 因為將 vuex 掛載到了 vue 實例中,所以 vuex 的數(shù)據(jù)可以通過 vue 實例訪問 ```javascript this.$store.state ``` **方法二:使用 mapState 函數(shù)將 vuex 數(shù)據(jù)映射為計算屬性** ```javascript // 1. 從 vuex 中按需導(dǎo)入 mapState 函數(shù) import { mapState } from 'vuex'; // 2. 使用 mapState 函數(shù)將 vuex 數(shù)據(jù)映射為當(dāng)前組件的計算屬性 (computed) export default { computed: { ...mapState(['token', 'userInfo']) } } // 也可以這樣寫 export default { computed: mapState(['token', 'userInfo']) } ``` #### 5. getters 的使用詳解 --- 使用場景: 當(dāng)某一個數(shù)據(jù)需要經(jīng)過一系列的操作后再返回時,可以使用 getters 處理 Getter 用于對 Store 中的數(shù)據(jù)進(jìn)行加工處理形成新的數(shù)據(jù),類似 Vue 的計算屬性(computed),起到一個包裝器的作用,當(dāng) Store 中的數(shù)據(jù)發(fā)生變化時, Getter 的數(shù)據(jù)也會跟著變化 getters 方法定義: ```javascript const store = new Vuex.Store({ state: { counter: 6 }, getters: { // 返回值是一個數(shù)據(jù) power(state) { return state.counter * state.counter }, // 返回值是一個函數(shù) add(state) { return (num) => { return state.counter + num } } } }) ``` **調(diào)用 getters 的兩種方式** 方式一、使用 `this.$store.getters.名稱` 調(diào)用 getters ``` // 調(diào)用方式 this.$store.getters.power // 在組件模板中調(diào)用 {{ $store.getters.power }} ``` getters 的其他用法參考下方代碼示例: ``` {{ $store.getters.more20Stu }} {{ $store.getters.more20StuCount }} {{ $store.getters.moreAgeStu(20) }} ``` ```javascript const store = new Vuex.Store({ state: { students: [ { id: 1, name: "wang", age: 18 }, { id: 2, name: "liang", age: 21 }, { id: 3, name: "zhang", age: 30 }, ], }, getters: { more20Stu(state) { // 獲取年齡大于20的 return state.students.filter(s => s.age > 20) }, more20StuCount(state, getters) { // 獲取年齡大于20的個數(shù) return getters.more20Stu.length }, moreAgeStu(state) { // 如果要傳參數(shù),需要返回一個函數(shù) return age => { return state.students.filter(s => s.age > age) } } } }) ``` 方式二、使用 mapGetters 將 getters 方法映射為當(dāng)前組件的計算屬性 ```javascript // 1. 從 vuex 中按需導(dǎo)入 mapGetters 函數(shù) import { mapGetters } from 'vuex'; // 2. 使用 mapGetters 函數(shù)將 getters 方法映射為當(dāng)前組件的計算屬性 (computed) export default { computed: { ...mapGetters(['power', 'total']) } } ``` #### 6. mutations 的使用詳解 --- vuex 的 state 數(shù)據(jù)更新的唯一方式: 提交 Mutation mutations 用于變更 store 中的數(shù)據(jù)。只能通過 mutations 變更 store 數(shù)據(jù),不能直接操作 store 數(shù)據(jù),這種方式雖然操作起來繁瑣一些,但是可以集中監(jiān)控所有數(shù)據(jù)的變化 mutation 主要包括兩部分: 事件類型 (type)、回調(diào)函數(shù) (handler) mutation 的定義方式: a. increment 稱為事件類型,回調(diào)函數(shù)的第一個參數(shù)永遠(yuǎn)就是 state b. mutations 中的方法第二個參數(shù)被稱為 mutations 的載荷 (payload) ```javascript { state: { counter: 1 }, mutations: { mutations: { increment(state) { state.counter++ }, decrement(state, num) { state.counter -= num }, } } } ``` **觸發(fā) mutation 的兩種方式** 方式一、使用 `this.$store.commit` 觸發(fā) mutations ```javascript // commit 的作用就是調(diào)用某個 mutation 函數(shù) this.$store.commit("increment");//不需要參數(shù) this.$store.commit("decrement", num);//傳參 ``` 通過 commit 進(jìn)行提交是一種普通的提交方式,vue 還提供了另外一種風(fēng)格,它是一個包含 type 屬性的對象 ```javascript this.$store.commit({ type: "add", num: 10, age: 20, }); ``` 此時要注意 mutation 中的方法的第二個參數(shù)的值,和普通提交方式可不一樣 ```javascript add(state, payload) { console.log(payload) // {type: 'add', num: 10, age: 20} } ``` 方式二、使用 `mapMutations` 將 mutations 函數(shù)映射為當(dāng)前組件的 methods 方法 ```javascript // 1. 從 vuex 中按需導(dǎo)入 mapMutations 函數(shù) import { mapMutations, } from 'vuex'; // 2. 使用 mapMutations 將 mutations 函數(shù)映射 methods 方法 export default { methods: { ...mapMutations(['login', 'getUserInfo']), } } ``` #### 7. actions 的使用詳解 ---- vuex 要求 mutations 中的方法體內(nèi)容必須是同步操作 主要的原因是當(dāng)我們使用 devtools 插件時,devtools 可以幫助我們捕捉 mutation 的快照,但如果是異步操作,那么 devtools 將不能很好的追蹤這個操作什么時候完成。 異步操作必須寫在 actions 中,通過觸發(fā) mutations 的方式間接變更數(shù)據(jù) 代碼示例: ```javascript const store = new Vuex.Store({ state: { counter: 6, }, mutations: { change(state, num = 1) { state.counter += num } }, actions: { update(context) { setTimeout(() => { context.commit('change') }, 1000) } } }) ``` **觸發(fā) actions 的兩種方式** 方式一、使用 `this.$store.dispatch` 觸發(fā) actions ```javascript // 不傳參數(shù) this.$store.dispatch("update"); // 傳遞參數(shù): dispatch 的第二個參數(shù) this.$store.dispatch("update", 10); // actions 方法在 commit 時也要將參數(shù)帶過去( payload 可以設(shè)置默認(rèn)值) update(context, payload = 1) { setTimeout(() => { context.commit('change', payload) }, } } ``` 因為 actions 方法都是異步操作,所以當(dāng)異步操作結(jié)束后應(yīng)該通知外面我已操作完成 ```javascript this.$store.dispatch("update", () => { console.log("異步操作結(jié)束"); }); actions: { update(context, payload) { setTimeout(() => { context.commit('change') payload() }, 1000) } } ``` 當(dāng)需要傳入?yún)?shù)時,可以這樣寫 ```javascript this.$store.dispatch("update", { payload: { name: "liang", age: 23, }, success: () => { console.log("異步操作結(jié)束"); }, }); actions: { update(context, params) { console.log(params.payload) setTimeout(() => { context.commit('change') params.success() }, 1000) } }, ``` 更優(yōu)雅的寫法 Promise: ```javascript this.$store .dispatch("update", { name: "liang", age: 23, }) .then((result) => { console.log("result: ", result); }); actions: { update(context, payload) { return new Promise((resolve, reject) => { console.log(payload) setTimeout(() => { context.commit('change') resolve('異步操作結(jié)束') }, 1000) }); } } ``` 方式二、使用 `mapActions` 將 actions 函數(shù)映射為當(dāng)前組件的 methods 方法 ```javascript // 1. 從 vuex 中按需導(dǎo)入 mapActions 函數(shù) import { mapActions } from 'vuex'; // 2. 將指定的 actions 函數(shù),映射為當(dāng)前組件的 methods 方法 export default { methods: { ...mapActions(['save', 'update']), } } ``` #### 8. modules 的使用詳解 --- vue 使用單一狀態(tài)樹,那么也就意味著很多狀態(tài)都會交給 vuex 管理,當(dāng)應(yīng)用變得非常復(fù)雜時,store 對象就有可能變得相當(dāng)臃腫,為了解決這個問題,vuex 允許我們將 store 分割成模塊,每個模塊擁有自己的 state、mutations、actions、getters 等 ``` // state {{ $store.state.a.name }} // getters {{ $store.getters.name }} {{ $store.getters.fullname }} // mutations this.$store.commit("updateName"); // actions this.$store.dispatch("save"); ``` ```javascript const store = new Vuex.Store({ state: { counter: 6, }, modules: { a: { state: { name: 'liang' }, mutations: { updateName(state) { state.name = 'zhang' } }, getters: { name(state) { return state.name + ' 666' }, // 在模塊的 getters 中有第三個參數(shù) fullname(state, getters, rootState) { return getters.name + ' ' + rootState.counter } }, actions: { save(context) { setTimeout(() => { context.commit('updateName') }, 1000); } } } } }) ``` #### 9. store 文件夾的目錄組織 --- 當(dāng)我們的 vuex 幫助我們管理過多內(nèi)容時,好的項目結(jié)構(gòu)可以讓我們的代碼更加清晰 官方文檔也給出了目錄結(jié)構(gòu)示例: [https://vuex.vuejs.org/zh/guide/structure.html](https://vuex.vuejs.org/zh/guide/structure.html) #### 10. vuex 用法總結(jié) --- 獲取 state 數(shù)據(jù)的兩種方式 ```javascript // 方式一、使用 this.$store.state.名稱 this.$store.state // 方式二、使用 mapState 將 state 數(shù)據(jù)映射為當(dāng)前組件的計算屬性 // 1. 從 vuex 中按需導(dǎo)入 mapState 函數(shù) import { mapState } from 'vuex'; // 2. 使用 mapState 函數(shù)將 vuex 數(shù)據(jù)映射為當(dāng)前組件的計算屬性 (computed) export default { computed: { ...mapState(['token', 'userInfo']) } } ``` 調(diào)用 getters 方法的兩種方式 ```javascript // 方式一、使用 this.$store.getters.名稱 this.$store.getters.power // 方式二、使用 mapGetters 將 getters 方法映射為當(dāng)前組件的計算屬性 // 1. 從 vuex 中按需導(dǎo)入 mapGetters 函數(shù) import { mapGetters } from 'vuex'; // 2. 使用 mapGetters 函數(shù)將 getters 方法映射為當(dāng)前組件的計算屬性 (computed) export default { computed: { ...mapGetters(['power', 'total']) } } ``` 觸發(fā) mutations 方法的兩種方式 ```javascript // 方式一、使用 this.$store.commit 觸發(fā) mutations this.$store.commit("increment") this.$store.commit("decrement", num) // 方式二、使用 mapMutations 將 mutations 函數(shù)映射為當(dāng)前組件的 methods 方法 // 1. 從 vuex 中按需導(dǎo)入 mapMutations 函數(shù) import { mapMutations, } from 'vuex'; // 2. 使用 mapMutations 將 mutations 函數(shù)映射 methods 方法 export default { methods: { ...mapMutations(['login', 'getUserInfo']), } } ``` 觸發(fā) actions 方法的兩種方式 ```javascript // 方式一、使用 this.$store.dispatch 觸發(fā) actions this.$store.dispatch("update") this.$store.dispatch("update", payload) // 方式二、使用 mapActions 將 actions 函數(shù)映射為當(dāng)前組件的 methods 方法 // 1. 從 vuex 中按需導(dǎo)入 mapActions 函數(shù) import { mapActions } from 'vuex'; // 2. 將指定的 actions 函數(shù),映射為當(dāng)前組件的 methods 方法 export default { methods: { ...mapActions(['save', 'update']), } } ```