1x0 Vuex 核心 - State & Mutations

发布于 2022年 02月 24日 18:15

Vuex 基础教程系列 🎉

Store

单一状态树与唯一数据源

单一状态树指的是用一个对象来包含整个应用中所有层级的状态(Vuex 的 store 对象就是一个典型的单一状态树)。

状态等价于数据、因此单一状态树的架构模型也非常符合与 ”唯一数据源(SSOT)“的标准。

SSOT (Signle Source Of Truth) 唯一数据源(单一事实来源),它属于信息系统的范畴。强调的是数据与信息的高度统一和集成(存在一个主系统来管理跨系统共享的数据,例如增、删、改、链接关系等),它不是具体的工具或方法,而是一种标准或者架构。

基于 SSOT 实现的系统其目的在于提供真实、相关和可参考的数据。其解决的痛点在一般企业管理系统中非常常见,比如通常对于一个企业而言,它会在不同的供应商除购买多种不同且相互独立的系统,这些系统很可能会存储同一个实体有关的重复数据,并且这些相关的数据不会被共享,当数据发生变更时其它系统也不会进行相应的自动更新。

具体案例的中,例如 ERP(企业资源规划)、CRM(客户管理系统)、物流仓储调度系统等它们都会需要客户这一实体的相关数据,例如客户的姓名、行业、联系方式、收获地址等等。如果每个系统都存储着自己的公共数据或实体相关数据的副本,那么当实体的数据发生变更时,便不能保证数据来源的唯一、可靠与真实。再比如,对于像电子健康档案中的记录更需要基于 SSOT 架构来存储以及返回患者的信息,以保证数据与信息的准确唯一。

单一状态树的优势表现于对状态的定位、获取和调试都会变得更加的简单。

在 Vue 组件中获取 Vuex 状态的几种方式

  1. 通过模块化导入然后直接读取 store.state 的值,或者将其转换为组件的计算属性。

    import store from '@/store'
    
    export default {
        computed: {
            count() { return store.state.count }
        },
        created() {
            this.$watch(() => store.state.count, (newValue) => {
                console.log(newValue)
            })
        }
    }
    
  2. 使用 Vuex 通过根组件向所有子组件注入的 $store 选项

    export default {
      template:`<div>{{$store.state.count}}</div>`,
      watch:{
            '$store.state.count': (val) => { console.log(val)}
      }
    }
    
  3. 使用 Vuex 提供的 mapState 工具方法可以将状态映射为组件实例的计算属性 (computed)。

mapState 工具方法

mapState 方法可以看做是对(方法一)的便捷操作,它可以批量的将 Vuex 状态映射为 Vue 组件的计算属性。

//对象取值
export default {
    computed: Vuex.mapState({
        count: 'count', //若计算属性名称与状态名称相同,则可以直接使用字符串的形式。
        foor: () => state.foor, //支持一个回调函数,且参数便是状态对象。
        bar: (state) => state.count + state.foor
    })
};

//数组取值
export default {
    computed: Vuex.mapState(['count', 'foor'])
}

如果计算属性成员的名称与 Vuex 中状态的名称相同,那么数组取值的方式将会更加的简单方便。

展开运算符

mapState 函数返回值是一个对象,为了更方便的引用这些状态,我们可以使用 ES6 的展开运算符将对象混入到 computed 选项中作为其直接的属性成员。

import { mapState } from "vuex";

export default {
    computed: {
				foor: 'foor',
				bar : 'bar',
        //使用对象展开运算符将 mapState 的值混入到 computed 选项中。
        ...mapState(['count'])
    }
}

在不支持 ES6 展开运算符的环境下则需要开发者自己编写对象的合并函数。

Mutations

提交与载荷

更改 Vuex 状态的唯一方式便是提交 Mutation,它非常类似于事件的概念,每个 Mutations 成员的 key 便是事件的类型(type),成员方法便是事件的处理方法 (handler),最后通过 store.commit(type) 的方式来触发事件处理方法的执行,从而进行状态的更改。

export default {
    state: { count: 0 },
    mutations: {
        increment(state) {
            ++state.count
        }
    }
}

store.commit('increment')

通过 store.commit 触发事件的同时你还可以传入一个额外的参数,即 Mutation 的载荷(payload)。载荷的形式有多种,可以是基本类型值,也可以是对象类型值。

store.commit('increment', 1); //基本类型的值
store.commit('increment', { count: 1 }) //对象类型值

对象形式的载荷还可以用一个 type 字段来替代 store.commit 中所要提交的事件类型。

store.commit({type:'increment',count:1})

在大多数情况下,载荷应该是一个对象,这样可以包含多个字段并且记录的 mutation 会更易读。

不论载荷的风格是何种形式,Mutation 接收载荷的格式都是固定的。

{
    mutations: {
        increment(state, payload){
            state.state = typeof payload === 'object' ? payload.count : payload;
        }
    }
}

mapMutations 工具方法

mapMutations 方法可以批量的将需要通过 store.commit 进行事件触发的操作映射为组件私有的 methods,以达到更方便调用的目的。

//对象取值
export default {
		template:'<button @click="incrementAlis">add</button>'
    methods: Vuex.mapMutations(
        {
            incrementAlis: 'increment',
            increment: 'increment'
        }
    ),
}

//数组取值
export default {
    methods: Vuex.mapMutations(['increment']),
}

Mutation 的相关规则

  1. 与状态变更相关的规则

    由于 Vuex 存储的状态实际上就是普通 Vue 组件实例上的响应式数据,所以再使用 Mutation 进行状态变更时必须要遵守与 Vue 相同的注意事项:

    1. 状态最好要在创建 Vuex 应用时就已经手动声明好。
    2. 对于后期需要动态添加的状态,请使用 Vue.set(obj, 'count', 1) 方法,或使用 ES6 展开运算符扩展原有的响应式对象。 state.obj = { ...state.obj, newProp: 123 }
  2. 使用常量替代 Mutation 事件类型

    在 Flux 架构中使用常量来替代事件类型名称是一种很常见的模式,其优点主要有两个方面:

    1. 对 Linter 之类的工具更加友好。
    2. 可以将这些常量统一抽离到一个公共文件中以使代码结构更加清晰,对合作的开发者更加友好,也让这个应用所包含的 Mutation 一目了然。
    // mutation-types.js
    export const SOME_MUTATION = 'SOME_MUTATION'
    
    // store.js
    import Vuex from 'vuex'
    import { SOME_MUTATION } from './mutation-types'
    
    const store = new Vuex.Store({
      state: { ... },
      mutations: {
        // 我们可以使用 ES2015 风格的计算属性命名功能来使用一个常量作为函数名
        [SOME_MUTATION] (state) {
          // mutate state
        }
      }
    });
    
  3. 必须要保证 Mutation 的成员方法必须是一个同步函数。

    只有满足了同步变更的前提,我们才能追踪状态变更的前后顺序,保证记录的准确性,因此 Mutation 的成员方法一定不能是一个异步方法。

    对于异步的事务变更我们可以使用 —— Action。

推荐文章