Store
单一状态树与唯一数据源
单一状态树指的是用一个对象来包含整个应用中所有层级的状态(Vuex 的 store
对象就是一个典型的单一状态树)。
状态等价于数据、因此单一状态树的架构模型也非常符合与 ”唯一数据源(SSOT)“的标准。
SSOT (Signle Source Of Truth) 唯一数据源(单一事实来源),它属于信息系统的范畴。强调的是数据与信息的高度统一和集成(存在一个主系统来管理跨系统共享的数据,例如增、删、改、链接关系等),它不是具体的工具或方法,而是一种标准或者架构。 基于 SSOT 实现的系统其目的在于提供真实、相关和可参考的数据。其解决的痛点在一般企业管理系统中非常常见,比如通常对于一个企业而言,它会在不同的供应商除购买多种不同且相互独立的系统,这些系统很可能会存储同一个实体有关的重复数据,并且这些相关的数据不会被共享,当数据发生变更时其它系统也不会进行相应的自动更新。 具体案例的中,例如 ERP(企业资源规划)、CRM(客户管理系统)、物流仓储调度系统等它们都会需要客户这一实体的相关数据,例如客户的姓名、行业、联系方式、收获地址等等。如果每个系统都存储着自己的公共数据或实体相关数据的副本,那么当实体的数据发生变更时,便不能保证数据来源的唯一、可靠与真实。再比如,对于像电子健康档案中的记录更需要基于 SSOT 架构来存储以及返回患者的信息,以保证数据与信息的准确唯一。
单一状态树的优势表现于对状态的定位、获取和调试都会变得更加的简单。
在 Vue 组件中获取 Vuex 状态的几种方式
- 通过模块化导入然后直接读取
[store.state](http://store.state.xxx)
状态的值,或者将其转换为组件的计算属性。 import store from '@/store' export default { computed: { count() { return store.state.count } }, created() { this.$watch(() => store.state.count, (newValue) => { console.log(newValue) }) } } 复制代码 - 使用 Vuex 通过根组件向所有子组件注入的
$store
选项 export default { template:`<div>{{$store.state.count}}</div>`, watch:{ '$store.state.count': (val) => { console.log(val)} } } 复制代码 - 使用 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 接收载荷的格式都是固定的。
代码语言:javascript复制{
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 的相关规则
- 与状态变更相关的规则
由于 Vuex 存储的状态实际上就是普通 Vue 组件实例上的响应式数据,所以再使用 Mutation 进行状态变更时必须要遵守与 Vue 相同的注意事项:
- 状态最好要在创建 Vuex 应用时就已经手动声明好。
- 对于后期需要动态添加的状态,请使用
Vue.set(obj, 'count', 1)
方法,或使用 ES6 展开运算符扩展原有的响应式对象。state.obj = { ...state.obj, newProp: 123 }
。
- 使用常量替代 Mutation 事件类型
在 Flux 架构中使用常量来替代事件类型名称是一种很常见的模式,其优点主要有两个方面:
- 对 Linter 之类的工具更加友好。
- 可以将这些常量统一抽离到一个公共文件中以使代码结构更加清晰,对合作的开发者更加友好,也让这个应用所包含的 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 } } }); 复制代码
- 必须要保证 Mutation 的成员方法必须是一个同步函数。 只有满足了同步变更的前提,我们才能追踪状态变更的前后顺序,保证记录的准确性,因此 Mutation 的成员方法一定不能是一个异步方法。 对于异步的事务变更我们可以使用 —— Action。