有時候我們需要從store中的state中派生出一些狀態,例如:
<div id="app"> <p>{{reverseMessage}}</p> </div> <script> const store = new Vuex.Store({ state:{reverseMessage:'Hello Vue!'} }) new Vue({ el:'#app', store, computed:{ reverseMessage:function(){return this.$store.state.reverseMessage.split('').reverse().join('')} } }) </script>
如果多個組件需要用到此屬性,我們要么復制這個函數,或者抽取到一個共享函數然后在多處導入它---無論哪種方式都不是很理想
writer by:大沙漠 QQ:22969969
Vuex允許我們在store中定義"getter"(可以認為是store的計算屬性),就像計算屬性一樣,getter 的返回值會根據它的依賴被緩存起來,且只有當它的依賴值發生了改變才會被重新計算
每個getter對應的匿名函數可以帶四個參數,分別是當前模塊的state、getter和根模塊的state、getter,例如:
<div id="app"> <p>{{reverseMessage}}</p> </div> <script> const store = new Vuex.Store({ state:{reverseMessage:'Hello Vue!'}, getters:{ reverseMessage:function(state){return state.reverseMessage.split('').reverse().join('');} } }) new Vue({ el:'#app', store, computed:{ reverseMessage:function(){return this.$store.getters.reverseMessage} } }) </script>
這樣在vuex內部就把reverseMessage這個屬性給實現了,還是很好用的,vuex官網里說我們可以把getter當作計算屬性一樣來使用,事實上vuex內部也是把getter定義為vue的computed計算屬性來實現的。
源碼分析
在創建Vuex.Store()初始化時會執行installModule()安裝根模塊,和getter相關的如下:
function installModule (store, rootState, path, module, hot) { //安裝模塊 /*略*/ module.forEachGetter(function (getter, key) { //遍歷module模塊的getters對象,如果找到了,則執行這個匿名函數 參數1:每個getter值 key:對應的鍵名 var namespacedType = namespace + key; //拼湊命名空間+鍵名,例如:a/computedCount registerGetter(store, namespacedType, getter, local); //依次執行registerGetter }); /*略*/ }
registerGetter用於注冊每個getter,如下:
function registerGetter (store, type, rawGetter, local) { //注冊getter if (store._wrappedGetters[type]) { //如果store._wrappedGetters下已經有key了 { console.error(("[vuex] duplicate getter key: " + type)); //則報錯,即不允許重復 } return } store._wrappedGetters[type] = function wrappedGetter (store) { //保存到store._wrappedGetters對應的type里 return rawGetter( //執行store函數 四個參數分別為local state、local getters、root state、root getters local.state, // local state local.getters, // local getters store.state, // root state store.getters // root getters ) }; }
這樣在 store._wrappedGetters中就存儲了對應的getter了,是一個匿名函數,函數有一個參數是store,這個是vuex.store()的實例,一會創建vue實例時會傳入的,這樣在geter里就能訪問到根模塊的state和getters了
例子執行到這里對應的_wrappedGetters如下:
最后Vuex走到resetStoreVM()去創建一個Vue實例時,和getter有關的邏輯如下:
function resetStoreVM (store, state, hot) { //重新存儲數據 var oldVm = store._vm; // bind store public getters store.getters = {}; var wrappedGetters = store._wrappedGetters; //獲取store的所有getter信息,也就是上面保存的數據,每個值是一個匿名函數 var computed = {}; //用於存儲最后的計算屬性 forEachValue(wrappedGetters, function (fn, key) { //遍歷wrappedGetters // use computed to leverage its lazy-caching mechanism computed[key] = function () { return fn(store); }; //將computed[key]定義為一個函數表達式,內部返回fn()執行后的結果,傳入store參數,這樣在geter里就能訪問到根模塊的state和getters了 Object.defineProperty(store.getters, key, { //設置store.getters的key的訪問器屬性,這樣就可以通過store.getters.aaa訪問到某個具體的值了 get: function () { return store._vm[key]; }, enumerable: true // for local getters }); }); /*略*/ }
之后如果有修改了state里的信息,getter里的信息都會自動更新的,這個歸功於Vue的響應式設計了。