说完了父子组件的通信,是通过props和$emit来实现的,那么非父子组件之间的通信又如何呢?有以下3种方式:bus总线,父链和子组件的索引;
bus总线
HTML部分:
<div id="container" v-cloak>
<p>{{message}}</p>
<!-- bus总线 -->
<my-component @hehe="alerMSg"></my-component>
</div>
javascript部分:
var bus = new Vue();//bus(中央事件总线)
//组件
Vue.component("my-component",{
template:"<button @click='setMessage'>点我</button>",
data:function(){
return {
msg:"今天,我们来看下非父子组件怎么通信"
}
},
methods:{
setMessage:function(){
bus.$emit("on-msg",this.msg);//由bus将该事件on-msg传递出去
this.$emit("hehe",this.msg)//父组件自定义事件
}
}
})
var myApp = new Vue({
el:"#container",
data:{
message:"hello,world!"
},
mounted:function(){
var _this = this;
// 在实例初始化的时候,监听该事件
bus.$on("on-msg",function(ele){//回调函数
_this.message = ele
})
},
methods:{
alerMSg:function(ele){
alert(ele)
}
}
})//实例
在上述例子中,首先由一个
空的Vue实例作为中央事件总线bus,当子组件的点击事件触发的时候,通过bus.$emit("actionName",[参数1],[参数2]...)将事件名‘actionName’的事件传了出去,因为在Vue实例myApp的
钩子函数mounted监听着来自bus的事件actionName,所以实例app就会接收到来自bus的事件,
进而在回调函数里执行自己的业务逻辑代码;
可见,无论是将"事件"传出去,还是监听传递过来的事件都离不开bus,所以bus就像是一个生活中的中介一样, 可以将以上的过程想象成一个对话:
当子组件的事件发生时告诉bus--赶紧把事件"on-msg"放了吧!bus听到后就通过bus.$emit将放了,然后Vue实例通过mounted侦听到这个消息后,就还是让bus把绑定在该事件下,该做的事情给做了就行了(执行回调函数)。
父链
HTML部分:
<div id="subContainer">
<p>{{notes}}</p>
<your-component></your-component>
</div>
javascript部分:
Vue.component("your-component",{
template:"<p @click='changeStr'>子组件</p>",
data:function(){
return {
NOTS:"实例中的数据也会被改变呢!"
}
},
methods:{
changeStr:function(){
this.$parent.notes = this.NOTS
}
}
})
var yourApp = new Vue({
el:"#subContainer",
data:{
notes:"我是实例中的数据"
}
})
所谓的父组件/子组件都是相对的,一个组件可能既是父组件也是子组件,就看相对于什么了--比如说:自定义组件当嵌套在一个实例中时是作为该实例的子组件的,而自定义组件本身又是template的父组件。
在子组件中可以通过this.$parent来直接访问该组件的父实例(或组件), 也就是说可以通过this.$parent来直接操纵父实例(或组件)中的数据,因为在子组件中this就代表组件本身,this.$parent就是“组件的父亲”--实例或者组件。
但是这样做并不好,因为你直接改变了父组件中的数据,这样就会导致父子组件的紧耦合?为什么呢,因为父组件中的数据是“源数据”,一旦改变了父组件中的数据,就会达到牵一发而动全身的后果(所有跟该数据相关的计算属性,判断等等都将受到影响), 所以父子组件的通信还是要靠props和$emit来实现比较好。
反之父实例也可以通过this.$children来访问该父实例下的所有子组件(不推荐);
子组件索引
HTML部分:
<div id="subsubContainer">
<p>{{subnotes}}</p>
<he-component ref = "hihi"></he-component>
<!--由"我是子组件中的数据"变成"我是实例中的数据,哈哈!" -->
<he-component ref = "lele"></he-component>
<!--由"我是子组件中的数据"变成"我是实例中的数据,哈哈!" -->
<he-component ref = "lala"></he-component>
<button @click="setSonCompoData">点我看效果</button>
</div>
javascript部分:
Vue.component("he-component",{
template:"<p>{{SUBNOTS}}</p>",
data:function(){
return {
SUBNOTS:"我是子组件中的数据"
}
}
})
var hisApp = new Vue({
el:"#subsubContainer",
data:{
subnotes:"我是实例中的数据,哈哈!"
},
methods:{
setSonCompoData:function(){
this.$refs.lele.SUBNOTS = this.subnotes;//
this.$refs.hihi.SUBNOTS = this.subnotes;
//第一,第二个子组件中的数据SUBNOTS变成了“我是实例中的数据,哈哈!”
}
}
})
一个父实例中可能嵌套有多个子组件,那么该如何快速的找到目标子组件呢?
就是通过在父实例(组件)中的子组件标签上使用ref属性指定一个名称,并在父实例(组件中)通过this.$refs.名称来访问对应的子组件(里面的数据更不在话下了),不过其仅仅是作为一种访问子组件的应急方案,尽量不要在模板template或者计算属性中使用$refs