Vuex
Vuex用于多组件之间的状态共享。
如果多个组件共享多个状态,那么这些组件之间的关系存在以下几种情况:
父子关系
兄弟关系
堂(堂堂...)兄弟关系
祖先后台(多级嵌套)关系
那么,在这些组件之间进行状态变更,则程序极有可能变得无比混乱。
1.先看vue的一个基本模式
这是vue的组件中,state、view、action三者之间的基本关系
状态驱动视图渲染
视图提供行为窗口
行为触发状态变更
这就是一个数据单向流动的模式。
2. 若两个组件之间有关联
如何处理两个组件之间有关联的case呢?Vue提供的组件通信方式:
父组件通过props向子组件传递state;
子组件通过event触发父组件更新state;
这个模式本质上还是遵循上图中数据单向流动的模式。只是将范围扩大到了两个组件之间,这两个组件共享一个状态。
const Parent = ponent("com-parent", {
template: `
current level {{leve}}
`,
data(){
return {
level: 1
}
},
methods: {
addLevel(){
this.level++;
}
}
})
const Child = ponent("com-child", {
template: `
current level {{leve}}
Alway less 1
addOne`,
props: ['level'],
methods: {
addOne(){
this.$emit('addlevel');
}
}
})
复制代码
3. 如果组件嵌套层级变深
如果嵌套层级数增加,则会产生如下case:
某个组件共享其祖先组件的状态;
两个有共同祖先的组件之间,共享同一个状态;
在第一种情况下,若组件借助于props/event的方式进行通信,则在两个组件之间的组件,仅仅只是做一些透传工作,跟组件自身的业务没有关系。
在第二种情况下,两个组件之间通信,都要绕道祖先组件进行通信;
当应用程序稍微复杂一些,便会使程序的可维护性变得微乎其微。
4. 一个补丁方案:事件总线
为解决以上问题, Vue提供了一种基于事件总线模式的方案 emit/emit/emit/on。
const Event = new Vue()
Event.$on('eventName', handler);
Event.$emit('eventName', data);
复制代码
这种方案本质上是借助一个第三方组件, 把所有的通信帮到该组件上然后进行业务处理,免去了在组件间进行不必要的状态传递。
但是把业务逻辑一个单独的第三方组件上,这种方式合适吗?
如果通信业务复杂,那么这个第三方组件也难免要变得臃肿和杂乱。而且打乱了Vue的基本模式——数据单向流动。
5. 一个比较普适的方案
为了应对复杂的状态共享场景,Vuex出现。与Vue结合后的模式:
在这种模式下,对Vue的单向数据流动可以概括为:
vuex state驱动视图更新
视图提供action
action调用mutation
mutation更改state
vuex限制了非mutation渠道对state的更改,同时带了一个附加收益,mutation的存在为debug提供了可能。
为什么使用了vuex,可以在vue组件中直接访问this.$store.state?
vuex借助vue的生命周期钩子beforeCreate,在每个vue组件实例化时,都添加了$store属性,它的值就是vuex实例自己。
vuex如何解决上面的问题
添加mutation,所有的数据变更都必需通过mutation。这样就把数据变更都收缩到一个口子上,便于数据的管控。
6. 简单场景下的跨级通信也要引入vuex吗
对于跨级通信的场景,如果只是简单的状态传递,就引入vuex,似乎有些杀鸡焉用牛刀的感jiao。其实vue从v2.4开始,提供了$attrs/$listeners两个新的属性,用于处理这种简单场景下的跨级通信。
7. 另外一种方案 provide/inject
除了$attrs/$listeners, Vue还提供了 provide/inject 做数据通信。
这就完了吗
上面说了这些,到底选择哪种数据通信方式,具体还要看实际业务场景。不管怎样,都是要考虑性价比的。