作为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"]
已经成功调取,是不是很方便。
文章评论