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的處理。
