原文地址: https://www.jeremyjone.com/543/, 轉載請註明
作爲Vue的狀態管理工具,Vuex的使用率相當之高。Vuex具有4個屬性,state,getters,actions,和mutations。
今天來討論一下getters
。它相當於vue的computed
計算屬性。每當state
中的值變化時,它也會自動更新。這個在我們需要那些稍微對state中的屬性進行改造
的屬性時很有幫助。在實際生產中,我們會大量使用getters
,而state
會相對較少。
getters的基本用法,直接調用
首先在根目錄下創建一個store.js
:
import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);
// 狀態對象
const state = {
myList: ["a", "b", "c", "d", "e"]
};
// getters計算屬性對象
const getters = {
getObjByIndex(state): {
return function(index) {
return state.myList[index];
};
}
};
// 包含多個用於更新state屬性的函數的對象
const mutations = {};
// 包含多個事件回調函數的對象
const actions = {};
export default new Vuex.Store({
state,
getters,
mutations,
actions
})
然後我們在main.js
中引用store
,並添加到Vue實例中:
import store from "@/store";
new Vue({
store, // 將store對象添加到Vue實例
render: h => h(App)
}).$mount("#app");
這樣就完成了最簡單的Vuex加載。
調用方案
加載之後,我們就可以在任何Vue組件中使用store
,具體方法:
this.$store.getters["getObjByIndex"](0) // 或 this.$store.getters.getObjByIndex(0)
因爲JavaScript的對象允許我們使用點的方式或者中括號的方式調用對象內容,所以這兩種方式都可以調出getters中的屬性信息。
- **注意:**我這裏使用了閉包的形態,因爲在
getters
中是屬性,不是函數,所以我們不能直接傳參調用,但是在實際使用中我們經常需要一些類似id
,index
這樣的參數來調用某一個序列中的某一個值。爲了解決這個問題,可以使用閉包的方案,具體閉包的功能這裏不贅述。
因爲Vuex
的store
本身就是一個對象管理,我們都可以通過直接調用的方式來操作。但是每一次都要寫this.$store.getters.xxx
這種形式真的很麻煩,有沒有簡單的方式呢?Vuex
給了我們很好的解決方案:mapGetters
。
getters的進階用法,使用mapGetters簡化代碼
對於Vue
的組件來說,我們可以使用mapGetters
函數,具體方法如下:
先看用法
mapGetters
是官方提供的map系列函數中的一個,它會直接返回一個對象,將我們需要的 對象 / 函數 直接加載到組件中。
單store
假設我們還是使用上述創建的store,那麼它在Vue組件中可以這樣使用:
import { mapGetters } from "vuex";
export default {
computed: {
...mapGetters(["getObjByIndex"]); // 導入getters的屬性
}
}
這樣就可以在這個Vue組件中使用這個屬性了。
getObjByIndex(0); // 會直接取值
分模塊store
如果我們的項目很大,單模塊的store
完全不能滿足我們的需求,這個時候就需要分模塊。
再比如我們現在有一個叫myStore
模塊的store
:
const state = {
myList: []
};
const getters = {
getObjByIndex(state): {
return function(index) {
return state.myList[index];
};
}
};
const mutations = {};
const actions = {};
export const myStore = {
namespaced: true,
state,
getters,
actions,
mutations
};
該倉庫的模塊名爲myStore
,繼續導入到Vue組件中:
import { mapGetters } from "vuex";
export default {
computed: {
// 這個時候就需要在導入時添加上模塊名
...mapGetters("myStore", ["getObjByIndex"]);
}
}
重命名
如果想重命名該屬性,可以使用如下語句,這個對於所有模塊都是一樣的:
import { mapGetters } from "vuex";
export default {
computed: {
// 對屬性重命名,這樣我們就可以在該組件中使用 getMyObj 了。
...mapGetters("myStore", {getMyObj: "getObjByIndex"});
}
}
深入理解
爲何我們可以這樣方便的使用mapGetters,我們可以通過源碼來找尋答案:
源碼很短:
/**
* Reduce the code which written in Vue.js for getting the getters
* @param {String} [namespace] - Module's namespace
* @param {Object|Array} getters
* @return {Object}
*/
var mapGetters = normalizeNamespace(function (namespace, getters) {
var res = {};
normalizeMap(getters).forEach(function (ref) {
var key = ref.key;
var val = ref.val;
// The namespace has been mutated by normalizeNamespace
val = namespace + val;
res[key] = function mappedGetter () {
if (namespace && !getModuleByNamespace(this.$store, 'mapGetters', namespace)) {
return
}
if (!(val in this.$store.getters)) {
console.error(("[vuex] unknown getter: " + val));
return
}
return this.$store.getters[val]
};
// mark vuex getter for devtools
res[key].vuex = true;
});
return res
});
具體來看
1、首先調用了通用的函數normalizeNamespace
,而這個函數就是將傳入的改成namespace/getters
的分割樣式,如果沒有namespace,那麼直接返回getters
,具體函數如下:
function normalizeNamespace (fn) {
return function (namespace, map) {
if (typeof namespace !== 'string') {
map = namespace;
namespace = '';
} else if (namespace.charAt(namespace.length - 1) !== '/') {
namespace += '/';
}
return fn(namespace, map)
}
}
2、迭代函數normalizeMap
只是將輸入的getters
對應成store的內部屬性,具體函數如下:
function normalizeMap (map) {
return Array.isArray(map)
? map.map(function (key) { return ({ key: key, val: key }); })
: Object.keys(map).map(function (key) { return ({ key: key, val: map[key] });
})
}
這段函數也說明了爲什麼我們可以使用對象來更改名稱,它依舊會映射我們傳入的value
。
3、查找屬性。之後的代碼就很簡單了,就是簡單的判斷+查找,如果找到對應的屬性,最後返回 this.$store.getters[val]
。
這就是mapGetters的大體工作流程。
學以致用
看過源碼,那麼我們試着不用mapGetters
來調用。聽起來沒什麼用,但是實際應用中,也會經常使用該方法,比如在js模型中,我們就需要手動調用。
首先引用store
,
import store from "@/store";
然後直接使用store
調用一開始我們定義的屬性:
var myObj = store.getters["myStore/getObjByIndex"]
已經成功調取,是不是很方便。