個人筆記-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