前面的话
在实际业务中,除了父子通信外,还有很多非父子组件通信的场景,非父子组件一般有两种,兄弟组件和跨多级组件。Vue.js 2.x中,使用一个空的Vue实例作为中央事件总线(bus)。
中央事件总线就好比一个中介,租房者与出租者担任两个跨级组件,租房者与出租者之间的联系全靠中介,而两个跨级组件之间的通信全靠中央事件总线(bus)。
中央事件总线
<div id="app1">
<component-a></component-a>
<component-b></component-b>
</div>
<script>
var bus = new Vue();
var app1 = new Vue({
el: '#app1',
components: {
'component-a': {
template: `
<p>{{message}}{{changeMsg}}</p>
`,
data() {
return {
message: ''
}
},
computed:{
changeMsg() {
var that = this;
// 监听来自bus实例的事件
bus.$on('on-message',function(msg) {
that.message = msg;
})
}
}
},
'component-b': {
template: `
<button @click="handleEvent">传递事件</button>
`,
methods: {
handleEvent: function() {
// 点击按钮通过bus把事件on-message发出去
bus.$emit('on-message','来自组件component-b的内容')
}
}
}
}
});
</script>
上例中组件component-a与component-b是兄弟关系: 首先创建了一个名为bus的空实例,作为中央事件总线。然后创建了 Vue实例app1, 局部定义了两个兄弟组件,在component-b组件中,点击按钮会通过bus把事件on-message发出去;在 component-a中用监听属性监听来自bus实例的事件。
除了中央事件bus外,还有两种方法可以实现组件间通信:父链与子组件索引.
父链
在子组件中,使用this.$ parent可以直接访问该组件的父实例或组件,父组件也可以通过this.$children访问它的所有 的子组件,而且可以递归向上或向下无线访问,直到根实例或最内层的组件。
<div id="app2">
<parent-component></parent-component>
</div>
<script>
var childNode = {
template: `
<button @click="handleEvent">通过父链直接修改数据</button>
`,
methods: {
handleEvent() {
// 访问父链后,可以做任何操作,比如直接修改数据
this.$parent.message = '来自组件child-component的内容'
}
}
}
var app2 = new Vue({
el:'#app2',
components: {
'parent-component': {
template: `
<div class="parent">
<p>{{message}}</p>
<child-component></child-component>
</div>
`,
data() {
return {
message: ''
}
},
components: {
'child-component': childNode
}
}
}
});
</script>
尽管Vue允许这样操作,但是在业务中,子组件应该尽可能避免依赖父组件的数据,更不该去修改它的数据,这样 使得父子组件紧耦合,父组件可以被任意组件修改,理想情况下,只有组件自己能修改它的状态。父子组件最好 还是通过props和$emit来通信。
子组件索引
当子组件较多时,通过this.children来一一遍历出我们需要的一个组件实例是比较困难的,尤其是组件动态渲染时,它们的序列是不固定的。Vue提供子组件索引的方法,用特殊的属性ref来为子组件指定一个索引名称。
<div id="app3">
<parent-component></parent-component>
</div>
<script>
var app3 = new Vue({
el:'#app3',
components: {
'parent-component': {
template: `
<div class="parent">
<button @click="handleRef">通过ref获取子组件实例</button>
<child-component ref="childCom"></child-component>
</div>
`,
components: {
'child-component': {
template: `
<div>子组件</div>
`,
data(){
return {
message: '子组件的内容'
}
}
}
},
methods: {
handleRef() {
// 父组件通过$ref来访问指定的实例
var msg = this.$refs.childCom.message;
console.log(msg);
}
}
}
}
})
</script>
在父组件模板中,子组件标签上使用ref指定一个名称,并在父组件内通过this.$refs来访问指定名称的子组件。
注意:$refs只在组件渲染完成后才填充,并且时非响应式的。它仅仅作为一个直接访问子组件的应急方案,应当避免在模板或计算属性中使用 $ refs