码字,杂谈

Vuex之Getters详解

《Vuex之Getters详解》

作为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中是属性,不是函数,所以我们不能直接传参调用,但是在实际使用中我们经常需要一些类似idindex这样的参数来调用某一个序列中的某一个值。为了解决这个问题,可以使用闭包的方案,具体闭包的功能这里不赘述。

因为Vuexstore本身就是一个对象管理,我们都可以通过直接调用的方式来操作。但是每一次都要写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"]

已经成功调取,是不是很方便。

点赞

发表评论

电子邮件地址不会被公开。 必填项已用*标注