vue项目开发中,父子、子父、兄弟等等组件之间的通信总是很讨厌的。除了父子可以使用ref很简单的搞定,其余的都显得不是那么方便,这点放到开发高阶组件中体现的更明显。
例如,我们目标是开发一个类似锚点跳转效果的组件,结构期望大致如下:
<anchor-scroll>
<trigger-view trigger-key="xxx">
</trigger-view>
<anchor-view anchor-key="xxx">
</anchor-view>
</anchor-scroll>
目的是希望通过点击trigger-view所包裹的元素,页面即滚动到anchor-view所包裹元素的位置(key一致,并被且需要被anchor-scroll包裹)。
那么,我们需要开发三个互相耦合的组件,面对的首要问题就是组件间事件的传递问题。仅三个组件组成的高阶组件,就包含了父子兄的全部场景。业务代码中,我们大可以通过$emit、$on、$refs,甚至bus实现。且不说麻烦的问题,在如上要求中实际子组件还有可能是孙组件、曾孙组件(因为实际场景中,我们肯定不能限制anchor-view外不可以包裹其他元素),那么这些方案是不可行的。
解决子到父通信
// 父组件
provide(){
return {
"anchorScroll": this
}
}
// 子组件
inject:["anchorScroll"]
利用provide和inject,使得父组件可以向所有组件注册实例,子组件只需通过this.anchorScroll即可获取父组件实例,然后操作数据及调用方法
解决兄弟组件间的通信
// 父组件 anchorScroll
data(){
return {
Bus:{}
}
}
// 子组件
mounted(){
this.anchorScroll.Bus[this.anchorKey] = this
},
所有子组件挂在完毕都将以自身key为key,将实例挂到父组件的Bus属性上。这样,只需要知道key值(anchor-scroll和anchor-view的key相同),然后不论层级多深,都可以通过this.anchorScroll.Bus[key]获取所需组件信息(注意不要在生命周期中调用,因为实例的赋值可能还未完成)。一句话总结就是:将所有名为anchor-view子孙组件的实例,都挂在共同的父组件上,然后通过父子通信完成兄弟组件间的通信
至此,父子兄的组件间通讯就完全打通了,就像武侠小说中打通了任督二脉一样,想怎么通信就怎么通信了。
文中提到的组件是我自己写的一个基于vue的PC端锚跳转组件,包含了scroll动画和目标区域提示等。 github地址:https://github.com/Emiya-wkq/vue-anchor-scroll/tree/master,欢迎star
gif预览