vuex 源码:深入 vuex 之 getter
cobish opened this issue · comments
前言
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 一样使用即可。