原文位址: 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"]
已經成功調取,是不是很友善。