個人筆記-vuex
最近想要沉淀下自己的知識體系,以前光看不記,當時記得,過段時間記憶就模糊了,好腦子不如爛筆頭,古人誠不欺我,所以現在決定給用自己的語言方式來給自己記個筆記。
vuex
vuex 有什么好講的呢,現在的組件通訊有太多方法了,但這種作為vue全家桶里的一員,使用還是蠻廣泛的,關于這方面的資料也很多,但我這個筆記是以自己的理解方式用大白話來講的,有不對的還講指正。(文章里的代碼摘自vuex的中文官網vuex.vuejs.org/zh/)
為什么要用vuex
我們知道,vue是基于組件化的,各管各的,如果這個時候,兄弟組件之間的需要共用某個數據,該怎么通訊呢,方法有很多,比如父子級別的可以通過props和$emit來解決,爺孫級別的可以通過$attrs和$listeners來解決,如果各自相互獨立的組件還可能通過中央總線事件來通訊,基本上這三種算是常見的了,當然,也有人說,可以做個全局的變量,然后大家都去拿,在大型項目開發中,這個變量你存了某個值,然后又被另一個人改了,你根本不知道是找哪個人哪行代碼改了,這個時候就需要我們進行一個約定,一個特定的地方,保存我們共同的東西,通過約定的方法去更改,以便我們能定位到是誰改了,傳了什么東西進來,又方便我們進行追蹤。vuex還有一個好處,他里面存的是當前的狀態,這就意味著,這里的發生了變化,能響應到對應的組件調用處。
共同的地方-Store
“store”基本上就是一個容器,它包含著你的應用中大部分的狀態 (state)。
Vue.use(Vuex) // 重點
const store = new Vuex.Store({state: {count: 0}
})
復制代碼
store實例里一定包含一個state對象,里就會存放所有的共有數據狀態。
那么組件里面我要去取到這個狀態該怎么做呢,首先要注入
const app = new Vue({el: '#app',// 把 store 對象提供給 “store” 選項,這可以把 store 的實例注入所有的子組件store,components: { Counter },template: `<div class="app"><counter></counter></div>`
})
復制代碼
在根組件里注入,這樣所屬組件就能通過this.$store.state
訪問到store實例里的state對象了,其實這種方法在vue里很常見,包括路由啊之類的,都是通過在根組件注入,所屬組件調用api的方式來取值的。
Getter
Getter有什么用呢?之前我們不是可以直接通過this.$store.state訪問到實例里的state對象了嗎,那還要這個有什么用呢,在我看來,Getter有三個作用
- 可以對this.$store.state里的某個屬性做計算,比如排序啊,過濾之類的
- 可以直接通過方法訪問,對state里的數據進行篩選,比如
store.getters.fun(id)
- 進行映射,這就要用到
mapGetters
這個輔助函數:
mapGetters
import { mapGetters } from 'vuex'export default {// ...computed: {// 使用對象展開運算符將 getter 混入 computed 對象中...mapGetters(['doneTodosCount','anotherGetter',// ...])}
}
復制代碼
這樣寫有什么好處呢,這其實是一個映射,可能不同的理解解釋的不一樣,大家也不用糾結,這樣寫了之后可以通過this.doneTodosCount
直接拿到,相當于把this.$store.state.doneTodosCount
映射成了this.doneTodosCount
,這樣大家就再也不用噼里啪啦寫一堆重復的東西了。
Mutation
之前我們提到,我們要約定一個方法去更新state里數據的狀態,這個約定的方法就是提交 mutation。
const store = new Vuex.Store({...mutations: {increment (state) {// 變更狀態state.count++}}
})
復制代碼
每一個mutation都類似一個事件,increment就是這個事件的type,你不能直接調用這個事件的回調,需要以相應的 type 調用 store.commit 方法:store.commit('increment')
有時候我們要需要對傳入的數據進加以第三方數據進行運算,文檔里叫提交載荷
// ...
mutations: {increment (state, n) {state.count += n}
}
復制代碼
調用
store.commit('increment', 10)
文檔里說載荷應該是一個對象,在大多數情況下,載荷應該是一個對象,這樣可以包含多個字段并且記錄的 mutation 會更易讀,這塊表示也蒙。。。
// ...
mutations: {increment (state, payload) {state.count += payload.amount}
}
復制代碼
還有一種對象提交模式,我覺得沒啥用,跳過。
mapMutations
同mapGetter,也是映射成this.的寫法
import { mapMutations } from 'vuex'export default {// ...methods: {...mapMutations(['increment', // 將 `this.increment()` 映射為 `this.$store.commit('increment')`// `mapMutations` 也支持載荷:'incrementBy' // 將 `this.incrementBy(amount)` 映射為 `this.$store.commit('incrementBy', amount)`]),...mapMutations({add: 'increment' // 將 `this.add()` 映射為 `this.$store.commit('increment')`})}
}
復制代碼
使用常量替代 Mutation 事件類型
這個看項目吧,大型項目可能需要,各種規范。
敲個黑板: mutation 都是同步事務,什么是同步事務呢,簡單的來說就是狀態變更都應該在此刻完成,不能有異步的操作,需要加入異步的操作見后面的Action知識點。
Action
Action 類似于 mutation,不同在于:Action 提交的是 mutation,而不是直接變更狀態。Action 可以包含任意異步操作。
const store = new Vuex.Store({...mutations: {increment (state) {state.count++}},actions: {/* es6解構寫法increment ({ commit }) {commit('increment')}*/increment (context) {context.commit('increment')}}
})
復制代碼
這個context是個什么東西呢,文檔上面是這么說的:與 store 實例具有相同方法和屬性的 context 對象,因此你可以調用 context.commit 提交一個 mutation,或者通過 context.state 和 context.getters 來獲取 state 和 getters。
Action 通過 store.dispatch 方法觸發:
store.dispatch('increment')
,同時Action里支持異步操作,舉個簡單的例子
actions: {incrementAsync ({ commit }) {setTimeout(() => {commit('increment')}, 1000)}
}
復制代碼
action里的寫法基本上和mutationu差不多
// 以載荷形式分發
store.dispatch('incrementAsync', {amount: 10
})// 以對象形式分發
store.dispatch({type: 'incrementAsync',amount: 10
})
復制代碼
mapActions
功能同之前的類似
組合 Action
組合Action,我個人用的不多,但原理要了解一下,Action支持異步,所以在Action里可以寫異步的commit,包括Promise,包括多個commit依賴性的先后觸發。
Module
大型項目多人會用到的東西,各自管各自的模塊。每個模塊都有自己對應的一套完整的store,然后再通過modules組合到頂層的store里去。
const moduleA = {state: { ... },mutations: { ... },actions: { ... },getters: { ... }
}const moduleB = {state: { ... },mutations: { ... },actions: { ... }
}const store = new Vuex.Store({modules: {a: moduleA,b: moduleB}
})store.state.a // -> moduleA 的狀態
store.state.b // -> moduleB 的狀態
復制代碼
對于module,我覺得只需關注幾個點就可了:
模塊內部的 mutation 和 getter,接收的第一個參數是模塊的局部狀態對象,而不是頂層的了。
const moduleA = {state: { count: 0 },mutations: {increment (state) {// 這里的 `state` 對象是模塊的局部狀態state.count++}},actions: {incrementIfOddOnRootSum ({ state, commit, rootState }) {if ((state.count + rootState.count) % 2 === 1) {commit('increment')}}},getters: {sumWithRootCount (state, getters, rootState) {return state.count + rootState.count}}
}
復制代碼
關于這塊,我覺得只需關注幾個點就Ok了。
現在的state都指局部的狀態對象了,頂級的狀態為rootState
const moduleA = {// ...getters: {sumWithRootCount (state, getters, rootState) {return state.count + rootState.count}},actions: {incrementIfOddOnRootSum ({ state, commit, rootState }) {if ((state.count + rootState.count) % 2 === 1) {commit('increment')}}}
}
復制代碼
參數和非module一樣,只不過多了第三個參數,這個參數指向頂級狀態對象。
命名空間
默認情況下,模塊內部的action ,mutation,getter是注冊在全局命名空間的–這樣使得多個模塊能夠對同一mutation或者action做出響應;如果希望你的模塊更加自包含或者提高可重用性,你可以通過添加namespaced:true 的方式使其成為命名空間模塊,當模塊被注冊后,他的所有getter,action,mutation都會自動根據模塊注冊的路徑調整命名;x