很早就聽說過vuex,但是一直沒用過。正好做到了登入這一塊,趁著這個機會掌握它。
一. 對Vuex的理解
Vuex 是一個專為 Vue.js 應用程式開發的狀態管理模式。 ---引用自Vuex官網
我個人的理解是這樣的,就拿登入來說,登入是一種狀態,在網站中有可能登入後的好多頁面和未登入的狀態都是不同的,把每個頁面想象成元件,也就是說多個元件都要共享這個登入狀態,如果是採用傳參的方式來共享,會變得很繁瑣,就像是多個函式巢狀一樣,要一層一層的將引數傳遞下去,而且不易維護,這時候就需要將這種多個元件共享的狀態或是資料抽取出來,單獨存放在一個地方,只要需要這些狀態或者資料的元件,都去這個地方找就好了,那麼實現這個功能的東西就叫做Vuex。
二. Vuex中的核心概念
-
state
state就是資料來源,也就是說所有共享的資料都存放在這個地方,例子:
使用vue-cli搭建的專案,會有一個store.js的檔案
import Vue from "vue"; import Vuex from "vuex"; Vue.use(Vuex); export default new Vuex.Store({ state: { isLogin: false // state裡面就可以放共享的資料 }, mutations: {}, actions: {} }); 複製程式碼
其他元件如果要獲取state裡面的資料,可以通過this.$store.state.xxx獲得
export default { name: "HelloWorld", props: { msg: String }, mounted() { console.log(this.$store.state.isLogin) }, }; 複製程式碼
-
mapState
假如一個元件需要獲取多個共享資料,例子:
// store.js export default new Vuex.Store({ state: { isLogin: false, name: "admin", password: 123456 }, mutations: {}, actions: {} }); 複製程式碼
// HelloWorld.vue export default { name: "HelloWorld", props: { msg: String }, computed: { isLogin() { return this.$store.state.isLogin }, name() { return this.$store.state.name }, passsword() { return this.$store.state.password } }, }; 複製程式碼
例子中是一個一個的寫出來,3個還好。假如有10個寫起來就很麻煩了。
mapState可以解決這個問題:
上面的例子用mapState寫就是這樣的:
// HelloWorld.vue import { mapState } from 'vuex'; export default { name: "HelloWorld", props: { msg: String }, computed: mapState(['isLogin', 'name', 'password']) }; 複製程式碼
mapState的引數如果是陣列的話,那陣列的每一項就是state中資料的key值,按照官網的說法是:對映 this.isLogin 為 store.state.isLogin。
但是computed不可能只有state裡的屬性,還有元件自身的屬性,這時候就要使用物件展開運算子了:
// HelloWorld.vue import { mapState } from 'vuex'; export default { name: "HelloWorld", props: { msg: String }, computed: { greetings() { return `${this.name}!你好` }, ...mapState(['isLogin', 'name', 'password']) } }; 複製程式碼
結果:
mapState還可以傳入一個物件,比如上面例子可以改寫成這樣
// HelloWorld.vue import { mapState } from 'vuex'; export default { name: "HelloWorld", props: { msg: String }, computed: { ...mapState({ isLogin: state => state.isLogin, name: state => state.isLogin, password: 'password', greetings(state) { return `${state.name}!你好` } }) } }; 複製程式碼
結果:
這兩種寫法都可獲得state中對應key值得資料
isLogin: state => state.isLogin password: 'password' 複製程式碼
依賴state中的計算屬性可以使用例子中函式的方式,
greetings(state) { return `${state.name}!你好` } 複製程式碼
注意不要使用箭頭函式,為了保證this指向正確(沒看過原始碼,不知道呼叫方式,所以不能確定this的指向,但是官方文件說不要使用箭頭函式,按照文件的來)
-
Getter
官方文件上有一句話我覺得已經解釋的很清楚了:
可以認為是 store 的計算屬性 ---引用自Vuex官網
直接看例子:
// store.js export default new Vuex.Store({ state: { todeList: [ { task: "追番", isDone: false }, { task: "玩遊戲", isDone: false }, { task: "睡覺", isDone: true } ] }, mutations: {}, actions: {} }); 複製程式碼
現在需要獲得todeList中已完成的事項數量。如果只有一個元件需要,那麼直接在元件內部filter就好了,如果是多個元件都要,那又出現了重複程式碼的情況,這種情況下可以使用getters解決:
// store.js export default new Vuex.Store({ state: { todeList: [ { task: "追番", isDone: false }, { task: "玩遊戲", isDone: false }, { task: "睡覺", isDone: true } ] }, getters: { doneList: state => { return state.todeList.filter(task => task.isDone); } } mutations: {}, actions: {} }); 複製程式碼
// HelloWorld.vue export default { name: "HelloWorld", props: { msg: String }, computed: { doneAmount() { return this.$store.getters.doneList.length; } } }; 複製程式碼
每當state中的資料變化,getter中依賴它的資料就會重新計算,在各個元件中也就不用重複的去寫那些方法了,直接獲取即可。
getters中的getter還有幾種其他的寫法:
-
getter可以引用其他getter。上面的例子可以改成這樣:
// store.js export default new Vuex.Store({ state: { todeList: [ { task: "追番", isDone: false }, { task: "玩遊戲", isDone: false }, { task: "睡覺", isDone: true } ] }, getters: { doneList: state => { return state.todeList.filter(task => task.isDone); }, doneAmount: (state, getters) => { return getters.doneList.length; } }, mutations: {}, actions: {} }); 複製程式碼
// HelloWorld.vue export default { name: "HelloWorld", props: { msg: String }, computed: { doneAmount() { return this.$store.getters.doneAmount; } } }; 複製程式碼
這樣改完之後元件裡直接用doneAmount就行了,getters中的doneAmount會根據doneList的改變而重新計算出數量。
-
getter可以返回一個函式,使用的時候就可以給getter傳引數了
// store.js export default new Vuex.Store({ state: { todeList: [ { task: "追番", isDone: false }, { task: "玩遊戲", isDone: false }, { task: "睡覺", isDone: true } ] }, getters: { getTask: state => taskName => { return state.todeList.find(taskItem => taskName === taskItem.task); } }, mutations: {}, actions: {} }); 複製程式碼
// HelloWorld.vue export default { name: "HelloWorld", props: { msg: String }, computed: { task() { return this.$store.getters.getTask('玩遊戲') } } }; 複製程式碼
同樣,getter也有輔助的函式mapGetters,它和mapState差不多,這裡就不繼續說了。
-
-
Mutation
想要改變state中的資料,在vuex中唯一的辦法就是提交一個mutation,直接看看它的使用方式吧,例子:
-
不帶引數
// store.js export default new Vuex.Store({ state: { num: 0 }, getters: {}, mutations: { addOne(state) { state.num++ } }, actions: {} }); 複製程式碼
// HelloWorld.vue export default { name: "HelloWorld", props: { msg: String }, mounted() { this.$store.commit('addOne') console.log(this.num) }, computed: { num() { return this.$store.state.num } } }; 複製程式碼
-
帶引數
// store.js export default new Vuex.Store({ state: { num: 0 }, getters: {}, mutations: { add(state, n) { state.num += n }, multiplication(state, payload) { state.num *= payload.num; } }, actions: {} }); 複製程式碼
// HelloWorld.vue export default { name: "HelloWorld", props: { msg: String }, mounted() { this.$store.commit('add', 10) this.$store.commit('multiplication', { num: 10 }) this.$store.commit({ type: 'multiplication', num: 10 }) console.log(this.num) }, computed: { num() { return this.$store.state.num } } }; 複製程式碼
帶引數方式中如果引數是物件,有兩種寫法,一種是把mutation的名字作為第一個引數,第二種是第一個引數是物件,物件中有個type屬性,type屬性的值就是mutation的名字。
mutation也有輔助函式mapMutations,上面例子可以寫成下面這樣:
// HelloWorld.vue import { mapMutations } from "vuex"; export default { name: "HelloWorld", props: { msg: String }, mounted() { this.add(10) this.multiplication({ num: 100 }) }, methods: { ...mapMutations([ 'add', 'multiplication' ]) }, computed: { num() { return this.$store.state.num } } }; 複製程式碼
mutation還需要注意的兩點有:
-
mutation事件名建議使用常量代替(官方推薦):
// 常量也建議全部存放在單獨的檔案中 const MUTATION_ADD = 'MUTATION_ADD' mutations: { [MUTATION_ADD](state, n) { state.num += n } } 複製程式碼
-
mutation 必須是同步函式,原因參考官網。
-
-
Action
由於mutation只能是同步函式,如果有非同步的操作,那麼就可以用action。例子:
export default new Vuex.Store({ state: { num: 1 }, getters: {}, mutations: { add(state, payload) { state.num += payload.num; } }, actions: { add(context, payload) { setTimeout(() => { context.commit("add", payload); }, 1000); } } }); 複製程式碼
action就好像吧mutation包了一層一樣,在action中就可以使用各種非同步了。 元件中使用action用的是這樣的方式:
this.$store.dispatch('xxx') this.$store.dispatch('xxx', {key: value}) this.$store.dispatch({ type: 'xxx', key: value }) 複製程式碼
action還可以這樣用:
actions: { actionOne() { return new Promise(reslove => { console.log("呆死ki"); reslove(); }); }, add(context, payload) { context.dispatch("actionOne").then(() => { setTimeout(() => { context.commit("add", payload); }, 1000); }); } } 複製程式碼
它的輔助函式叫mapActions,用法和mapMutations類似。
最後剩Module,目前還沒用過,所以理解可能會有問題。這裡就不說了。
總結下就是:
vuex中:
- store:作為一個容器,裡面包含了state,mutation等等,就像一個vue的例項一樣。
- state:store中的資料來源。
- getter:store中的計算屬性
- mutation:修改state的唯一方法就是提交一個mutation,mutation只能是同步函式
- action:由於mutation只能是同步函式,那麼有非同步的操作就要用到action,action就好像將mutation包裹了一層一樣。
- module: 如果store特別的龐大,可以使用module將它分割成模組。
-