天天看點

Vuex之Getters詳解

原文位址: https://www.jeremyjone.com/543/, 轉載請注明

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

    中是屬性,不是函數,是以我們不能直接傳參調用,但是在實際使用中我們經常需要一些類似

    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"]
           

已經成功調取,是不是很友善。

繼續閱讀