vue之Vuex原理架構解析與應用

狀態管理模式

在單頁應用的開發中,Vue全家桶的Vuex和React全家桶的Redux都是統一的狀態管理工具/模式。它們是全局的,簡單來講,就是在store中定義一個數據,就能夠在整個項目中獲取、修改,而且修改會獲得全局的響應變動;html

這些狀態管理工具適用於中大型項目,在小型項目會使得項目變得繁雜;vue

Vuex架構

先上一張Vuex官網的架構圖:vuex

vuex

常見的目錄結構

  • src
  • store
    • modules // 模塊定義store
      • module.js // 單個模塊
    • index.js // 用於引入各個單一模塊,new一個store
    • types.js // 定義常量

State

Vuex使用的是單一狀態樹,即state是一個對象,包含了全部定義的數據。redux

在單一的模塊module.js中,咱們能夠這樣定義一個數據:api

const person = {
    state: {
        name: "tom",
        age: 20
    }
};

export default person;
複製代碼

而後由index.js統一引入,實例化一個新的store:緩存

import Vue from "vue";
import Vuex from "vuex";
import person from "@/store/modules/module";

Vue.use(Vuex);

const store = new Vuex.Store({
    modules: {
        person
    }
});

export default store;
複製代碼

最後在main.js中注入全局:架構

import Vue from "vue";
import store from "@/store";

new Vue({
    store
});
複製代碼

state值獲取 & mapState

咱們須要person的信息時,使用this.$store.state.person便可獲取。異步

單當統一組件屢次須要person、person1的信息時,這時候使用mapState函數就很很方便。其中mapState中的函數接收的參數是該模塊局部狀態對象stateasync

// vuex自帶的mapState api
import { mapState } from "vuex";

export default {
    // ...
    computed: {
        getTest() { return "test" },

        mapState({
            // 箭頭函數可以使代碼更簡練
            person: state => state.person,

            // 傳字符串參數 'person' 等同於 `state => state.person`
            personAlias: 'person',

            // 爲了可以使用 `this` 獲取局部狀態,必須使用常規函數
            personName(state) {
                return state.person.name + this.localPerson.name
            }
        })

        // 對象展開運算符更方便
        // ...mapState({
        // person: state => state.person
        // })

        // 對象展開運算符加名稱縮寫究極方便
        // ...mapState([
        // "person"
        // ])
    }
}
複製代碼

Getters

有時候咱們不單單須要state的值,多個組件須要這個值的衍生值,如將數值由人民幣換算成美圓,這時候就是getter出場的時候了,這一點它有點相似compute,getters返回值會根據它的依賴被緩存起來,且只有當它的依賴值發生了改變纔會被從新計算。函數

getter接受 state 做爲其第一個參數,接受 getters 做爲其第二參數,對於模塊內部的getter來講,接受rootState做爲第三個參數,爲根節點的state。

const person = {
    state: {
        name: "tom",
        age: 20,
        money: 100
    },
    getters: {
        getAge(state) {
            return state.age / 10;
        }

        convert(state, getters, rootState) => {
            // 第二個參數getters返回該store.getters對象
            return state.money * 100 * getters.getAge;
        },

        // getter也能夠返回函數,以達到傳參的目的
        // 1:人民幣轉美圓 0:反之
        convertByRate: (state) => (type) => {
            const rate = type === 1 ? 6.8925 : 0.1451;
            return state.money * rate;
        }
    }
};
複製代碼
// getter傳參
// 轉美圓
this.$store.getters.convertByRate(1);
複製代碼

mapGetters

用法和mapState相同,將 store 中的 getter 映射到局部計算屬性

mapGetters中方法接受一個參數:context對象,等同於store實例

// 組件中:
import { mapGetters } from "vuex";
// ...
computed: ...mapGetters({
    convert: context => context.getters.convert
});
// computed: ...mapGetters([
// "convert"
// ]);
複製代碼

Mutations 同步事務

Vuex中,在組件中直接改變state的值是不行的,惟一改變狀態的方法是提交 mutation 。每一個 mutation 都有一個字符串的 事件類型 (type) 和 一個 回調函數 (handler),這是官方的說法,我以爲其實就是事件名和事件本體啦,這樣更好理解。事件第一個參數是state,第二個參數是載荷(payload),可選,通常狀況下是一個對象。(載荷payload這個名字也是頗有逼格,redux中也是這樣叫的,vuex參考了redux)

仍是以前的例子:

const person = {
    state: {
        name: "tom",
        age: 20,
        money: 100
    },
    getters: {
        getAge(state) {
            return state.age / 10;
        }
    },
    mutations: {
        setName(state, payload) {
            state.name = payload.name;
        }

        // 或者簡單點,不傳對象直接傳值
        // setName(state, val) {
        // state.name = val;
        // }
    }
};
複製代碼

那麼怎麼調用這個mutation來改變state的值呢?

this.$store.commit("setName", { name: "rachael" });

// 對象的形式調用
this.$store.commit({
    type: "setName",
    name: "rachael"
});
複製代碼

mapMutations

同mapState 與 mapGetters用法,咱們將mutation的方法映射到本地組件中;

import { mapMutations } form "vuex";

export default {
    methods: {
        ...mapMutations([
            // 將this.setName(payload) 映射爲 this.$store.commit("setName", payload)
            "setName"
        ])
    }
}
複製代碼

在mutations中只支持同步事件,須要處理一些http請求等異步事件時就須要actions了

Actions 異步事務

在action中,不是直接改變狀態的值,而是提交mutation來改變。action中能夠包含任意的異步操做。

仍是以前的例子:

const person = {
    state: {
        name: "tom",
        age: 20,
        money: 100
    },
    getters: {
        getAge(state) {
            return state.age / 10;
        }
    },
    mutations: {
        setName(state, payload) {
            state.name = payload.name;
        }
    },
    actions: {
        setName(context) {
            // context參數與 store 實例具備相同方法和屬性,當不等同於store實例,其還有rootState屬性,即根節點state
            setTimeout(() => {
                context.commit("setName", { name: "rachael" });
            }, 1000)
        },

        // 參數解構簡寫
        // setName({ commit, rootState }) {
        // rootState爲該模塊根節點state值
        // commit("setName", { name: "rachael" });
        // }
    }
};
複製代碼

調用action

this.$store.dispatch('setName', { name: "rachael" });
複製代碼

一樣也有mapActions函數,用法和mapMutations相同;

當一個action函數須要調用其餘異步的action1時,將action1返回一個Promise並結合async / await調用便可。

命名空間

默認狀況下,模塊內部的 action、mutation 和 getter 是註冊在全局命名空間的。在調用時,直接全局調用:

this.$store.getters.getAge();
this.$store.commit("setName", { name: "rachael" });
this.$store.dispatch("setName", { name: "rachael" });
複製代碼

註冊爲模塊命名空間的就不討論了,通常用默認的全局就能夠了,模塊命名空間會麻煩不少。

常量表示

使用常量代替方法名,再把這些常量放在統一的一個文件:types.js中,有助於咱們維護和理解。

types.js:

export const SET_NAME = "SET_NAME";
export const GET_NAME = "GET_NAME";
複製代碼

在module.js中就能夠改成:

import {
    SET_NAME,
    GET_NAME
} form "@/store/types";

const person = {
    state: {
        name: "tom",
        age: 20,
        money: 100
    },
    getters: {
        [GET_NAME](state) {
            return state.age / 10;
        }
    },
    mutations: {
        [SET_NAME](state, payload) {
            state.name = payload.name;
        }
    },
    actions: {
        [GET_NAME]({ commit, rootState }) {
            // context參數與 store 實例具備相同方法和屬性,當不等同於store實例,其還有rootState屬性,即根節點state
            setTimeout(() => {
                commit("setName", { name: "rachael" });
            }, 1000)
        }
    }
};
複製代碼
this.$store.getters.[GET_NAME]();
this.$store.commit("SET_NAME", { name: "rachael" });
this.$store.dispatch("SET_NAME", { name: "rachael" });
複製代碼

總結

再回到這張圖,是否是就好理解了~

vuex

store

首發於本人的博客:柴犬工做室

相關文章
相關標籤/搜索