前言
vuex 把 getter 比作是 store 的計算屬性 。就像 vue 的計算屬性一樣,getter 的返回值會根據它的依賴被快取起來,且只有當它的依賴值 data 發生了改變才會被重新計算。
其實,知道了 state 是 _vm 例項中的 data,那首先可以猜測 getter 就是 computed。那麼,我們就來驗證一下這個猜測是否是正確的。先說結論,當然是正確的啦哈哈~
注:本次閱讀的是 vuex 的 2.0.0 版本,原始碼請戳 這裡。
解讀
跟解讀 state 一樣,getter 是 store 物件的屬性,所以依然從 Store 這個類開始入手。
還是開始看建構函式 constructor,發現裡面並沒有 getter 的程式碼,但有兩個方法,進去後發現有相關的程式碼,於是程式碼簡化為:
constructor (options = {}) {
// and collects all module getters inside this._wrappedGetters
installModule(this, state, [], options)
// initialize the store vm, which is responsible for the reactivity
// (also registers _wrappedGetters as computed properties)
resetStoreVM(this, state)
}
複製程式碼
接下來的任務就是從 installModule
和 resetStoreVM
兩個方法中找到 getter 的實現即可。
installModule
先來看 installModule
。簡化了程式碼後,發現跟 getter 相關的只有 wrapGetters
方法:
function installModule (store, rootState, path, module, hot) {
const isRoot = !path.length
const {
getters
} = module
if (getters) {
wrapGetters(store, getters, path)
}
}
複製程式碼
進入 wrapGetters
方法,我們會發現這個方法的實現是為了拼接 store 的一個 _wrappedGetters
物件。這樣拼接的目的是為了可以在需要執行 getter 裡的方法時,還能傳想傳的引數再執行。我們暫時忽略 modulePath 與一些判斷,簡化程式碼為:
function wrapGetters (store, moduleGetters) {
Object.keys(moduleGetters).forEach(getterKey => {
const rawGetter = moduleGetters[getterKey]
// 將 options 裡的 getter 賦值到 _wrappedGetters
// 因為 computed 的賦值就是 return 一個函式
store._wrappedGetters[getterKey] = function wrappedGetter (store) {
return rawGetter(
store.state, // local state
store.getters, // getters
store.state // root state
)
}
})
}
複製程式碼
resetStoreVM
專門為 store 拼接了一個 _wrappedGetters
物件有啥好處呢?別急,我們先看另一個方法 resetStoreVM
,還是過濾掉與 getter 不相關的程式碼:
function resetStoreVM (store, state) {
// bind store public getters
store.getters = {}
// 獲取剛剛拼接的 _wrappedGetters
const wrappedGetters = store._wrappedGetters
// 開始拼接 computed
const computed = {}
Object.keys(wrappedGetters).forEach(key => {
const fn = wrappedGetters[key]
// use computed to leverage its lazy-caching mechanism
computed[key] = () => fn(store)
Object.defineProperty(store.getters, key, {
get: () => store._vm[key]
})
})
// use a Vue instance to store the state tree
store._vm = new Vue({
data: { state },
computed
})
}
複製程式碼
裡面就用到了剛剛拼接的 _wrappedGetters
物件。先看方法裡下面的 store._vm,我們猜測的沒錯吧,果然是 computed。那麼上面的程式碼就是拼接一個 computed 物件了。以下這行程式碼就是拼接一個 Vue 能是識別的計算屬性 computed。
computed[key] = () => fn(store)
複製程式碼
並使用 Object.defineProperty
對 store.getters 的 get 方法進行重寫。這樣,一旦訪問了 this.$store.getters.count
,那麼 get 方法就會返回 this.$store._vm.count
,也就是 _vm 的計算屬性 count。
所以, store.getters 實際上就是 store._vm 的計算屬性 computed。
總結
之前解讀了 state,再加上本篇的 getter,我們已經知道了 state 和 getter 分別對應著 store._vm 例項的 data 和 computed。所以下次再使用到 getter,我們可以把它當成 vue 的 computed 一樣使用即可。