一、一个错误引发的思考
在 Weex 子组件中通过 this.$parent.$emit('event')
触发父组件的事件,父组件通过 this.$on('event', handler)
接收事件,从而实现父子组件的通信。这在 Native 端表现正常,然而当使用 Vue-loader
编译代码,引入 vue-runtime
和 weex-vue-render
在浏览器端运行的时候,事件无法正常捕获,这是什么原因呢?
假设把前文所说的父组件命名为 father, 子组件命名为 child,起初, Weex 中:
child.$parent === father // true
我们为了通知 father 去做某件事情,可以在 child 中使用 this.$parent.$emit
,可是当解析到浏览器执行的时候,Debugger 发现:
child.$parent === father // false
child.$parent.$parent === father // false
也就是在 father 和 child 之间,多了一层组件的封装,之前的父子关系发生了改变,事件自然无法正常传达
这是因为,在 Weex 中有很多内建组件,底层基于 Native 端实现,当通过 vue-runtime
在浏览器运行的时候,需要将这些内建组件用基于浏览器的 Vue 组件代替,而这些 Vue 组件要实现类似底层 Native 组件的效果,极有可能需要封装多层,也就是会出现上述的父子关系变更的情况
可以认为是 Weex 做得不好的地方,但是我觉着,就算没有这个错误,也应该避免使用
this.$parent
二、this.$parent 背离了组件解耦的原则
上述的组件通信方式,是从哪来的奇淫巧技,我都已忘记,目前在 Vue 官网上找不到这种使用方法,也就是连官方都不推荐了更印证了不应该使用 this.$parent
的说法。
其实原因也很简单,子组件引用父组件的实例,这种强关联背离了组件的解耦原则,子组件依赖于某一特定的父组件,那么这个子组件只适用在这个父组件下,把这个子组件放到别处,就无法正常运行(或者有功能的缺失),并且一旦出现章节一中父子关系变更的情况,结果更加难以预料
那么子父组件确实是需要通信的时候,不使用 this.$parent
有什么替代方案么?答案是有的,Vue 中有个 bus 总线,子组件通过 bus.$emit
把事件发到总线中,任何组件都可以监听这个事件。也就是子组件只负责触发事件,并不需要关心具体的处理组件。总线接收到事件信号后,发送到监听了该事件的组件处理
对于父到子的通信,父组件通过 props 向子组件传入参数,对于这个方向的数据流,我们却不用太担心的解耦的问题,因为父组件作为调用方只需要传递子组件需要的参数即可,子组件只需要关心参数,并不需要关心是谁在调用,同样可以顺利移植,完成解耦
从这个层面来说,从某种意义上说,单向数据流促进了组件间的解耦
三、这同样可以解释为什么我们需要 Vuex
以这个角度来观察 Vuex 以及所有单向数据流状态控制框架,是不是某种意义上的事件总线呢?
bus.png store.png其实 Vuex 不过是 Bus 更高级细致的实现而已,连官方文档也说了,有必要好好思考下你是否真的需要 Vuex,如果你的应用本身不复杂,其实只需要一个简单的 Bus 就够了
网友评论
1. 解耦也有个范围,在应用内部,集中管理数据的话,达到了内部解耦,当然你剥离了 store 是不能允许的(通过传入属性的组件除外),否则继续往下抬杠的话,脱离了 vue-runtime 你的 vue 代码能跑?
2. 其次,不是说父子通讯背离了解耦,而是子对父的定向控制背离了解耦,注意方向,是子对父
3. 全局变量,数据总线,vuex 其实是同一个原理,按照你的说法,使用 全局变量 去替代 vuex ,难道就可以剥离全局变量跑?这个不能够吧
vuex 是 数据总线的一种高级形式,数据总线呢,又是全局变量的一种设计方法,文章也说了,如果你的应用足够简单,你可以好好思考下是否需要 vuex。
换句话说,使用 vuex 来复杂应用的数据状态,要比你用零散的全局变量来管理要清晰,仅此而已,简单的时候,则大可不必