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

首发于本人的博客:柴犬工做室

相关文章
相关标签/搜索