1. 准备工作
1) 创建一个store,state只包含一个count成员:
new Vuex.Store({ state: { count: 0 }, mutations: { increment(state) { state.count++; } }, actions: { incrementOfActive: ({ commit }) => { commit('increment'); } }, getters: { countOfGetter: (state) => { return state.count; } } });
2) 创建一个Vue对象,并设置store
window.DEMO_VM = new Vue({ el: '#app', store });
2. Vuex对getter做了哪些处理
通过阅读Vuex的源码,发现对getter做了下面处理
2.1 进行局部化
说明:当创建Vuex.Store时,如果内部嵌套了多个stroe(Module),将进行局部化,目的是为了Module各自的getters都保持对各自state的访问。
代码:
function registerGetter(store, type, rawGetter, local) { if (store._wrappedGetters[type]) { if (__DEV__) { console.error(`[vuex] duplicate getter key: ${type}`); } return; } store._wrappedGetters[type] = function wrappedGetter(store) { return rawGetter( local.state, // local state local.getters, // local getters store.state, // root state store.getters // root getters ); }; }
2.2 注册为计算属性
说明:初始化内部变量_vm,将getter注册为_vm的计算属性。
// 获取getters内的每个getter封装到_vm const wrappedGetters = store._wrappedGetters; const computed = {}; forEachValue(wrappedGetters, (fn, key) => { computed[key] = partial(fn, store); Object.defineProperty(store.getters, key, { get: () => { return store._vm[key]; }, enumerable: true // for local getters }); }); store._vm = new Vue({ data: { $$state: state }, computed });
3. 解析逻辑
3.1 $store.getters.countOfGetter值从哪里来
说明:虽然我们创建的countOfGetter的内部为"return state.count;",难道我们每次调用countOfGetter都是执行了对应的函数吗?
从2.2节的源码上看,Vuex对countOfGetter设置了get特性,每次调用都是从_vm.countOfGetter获取。
所以当调用 window.DEMO_VM.$store.getters.countOfGetter 时 → 实际上从返回为 window.DEMO_VM.$store._vm.countOfGetter。
这里要注意一点store.getters都被注册为了_vm的计算属性。
官方对计算属性好处介绍:“计算属性是基于它们的响应式依赖进行缓存的。只在相关响应式依赖发生改变时它们才会重新求值。”
总结:这样子也进一步说明store.getters保留了缓存特性。
3.2 修改了state,getters为什么会变更
说明:跟上面的说的getters注册为计算属性一样,_vm绑定了state与getters对应的关系,当变更state时,对应的getter也会发生改变。
4. 问题
4.1 为何不直接在组件外修改state?
既然可以通过"window.DEMO_VM.$store.state.count = 4" 这样操作来修改state,为什么还需要mutations、actions?
有下面几个原因:
1) 统一规范。如果都像这样在外部直接修改state,那么整个Vuex体系就会乱掉,直接当成了全局变量(缓存)来使用了。
2) hooks:mutations、actions内部的调用了都附加了Subscribe的处理。