vue组件间通信

父组件向子组件通信

vue中父组件向子组件通信是通过props属性实现的,用户需要在子组件的vue对象中设置props属性。在使用这个属性的时候,应该避免在子组件中修改props的值,vue是不建议这么做的

<!-- 父组件 -->
<template>
	<div class="container">
		<button class="btn" @click="sendMsg">给子组件发送消息</button>
		<div class="words">
			<child :parentMessage="parentMessage"></child>
		</div>
	</div>
</template>
<script>
	import Child from './Child';
	export default {
		name:  'Parent',
		data() {
			return {
				parentMessage: ''
			}
		},
		components: {
			Child
		},
		methods: {
			sendMsg() {
				this.parentMessage = '父组件发来一条消息'
			}
		}
	}
</script>


<!-- 子组件 -->
<template>
	<div class="container">
		<div class="words" v-if="parentMessage">{{ parentMessage }}</div>
	</div>
</template>
<script>
	export default {
		name: 'Child',
		props: ['parentMessage'],
	}
</script>

子组件向父组件通信

1.emit事件触发的方式

准确来说,在这种方法中,子组件没有向父组件传递数据,只是子组件触发父组件监听的事件。我们可以在子组件中自定义事件,然后在父组件中监听子组件发过来的事件

<!-- 子组件 -->
<template>
	<div class="container">
		<button @click="sendMsg">给父组件发送消息</button>
	</div>
</template>
<script>
	export default {
		name: 'Child',
		props: ['parentMessage'],
		data() {
			return {
				childMessage: ''
			}
		},
		methods: {
			sendMsg() {
				this.$emit('finish')
			}
		}
	}
</script>

<!-- 父组件 -->
<template>
	<div class="container">
		<child @finish="receive"></child>
		<p>{{ childMessage }}</p>
	</div>
</template>
<script>
	import Child from './Child';
	export default {
		name: 'Parent',
		data() {
			return {
				childMessage: ''
			}
		},
		components: {
			Child
		},
		methods: {
			receive() {
				this.childMessage = '子组件呼叫'
			}
		}
	}
</script>

2.通过回调函数实现

前面我们用props属性来实现父组件向子组件通信,实际上,我们也可以使用props来实现子组件向父组件的通信

<!-- 父组件 -->
<template>
	<div class="container">
		<child @finish="receive" :son="son"></child>
	</div>
</template>
<script>
	import Child from './Child';
	export  default {
		name: 'Parent',
		data() {
			return this.childMessage = ''
		},
		components: {
			Child
		},
		methods: {
			receive() {
				this.childMessage = '子组件呼叫'
			},
			son() {
				this.receive()
			}
		}
	}
</script>

<!-- 子组件 -->
<template>
	<div class="container">
		<button @click="son">给父组件发送消息</button>
	</div>
</template>
<script>
	export default {
		name: 'Child',
		props: ['son']
	}
</script>

3.通过$refs实现

父组件可以通过$refs来获取子组件的属性和方法,但是$refs不是响应式的,因此不要视图用它在模板中做数据绑定。另外,vue的数据更新是异步的,我们需要等待DOM更新完成后才进行DOM元素数据读取。那么,按照这个思路,我们在父组件中获取$refs的值时需要放在created的$nextTick或mounted中

<!-- 父组件 -->
<template>
	<child ref="childName"></child>
</template>
<script>
// ...省略一些内容
  created() {
    this.$nextTick(() => {
      this.childMessage += this.$refs.childName.$data.childData;
      this.childMessage += ' - ' + this.$refs.childName.childRef();
    })
  },
  /*
  mounted() {
    this.childMessage = this.$refs.childName.$data.childData;
    this.childMessage += " - " + this.$refs.childName.childRef();
  }*/
</script>

<!-- 子组件 -->
<script>
	// ... 省略一些内容
	data() {
		return {
			childData: "子组件通过ref向父组件通信1"
		}
	},
	methods: {
		childRef() {
			return "子组件通过ref向父组件通信2"
		}
	}
</script>

兄弟组件通信

1.通过一个共同的父组件进行通信

<!-- 父组件 -->
<template>
	<div class="container">
		<brother-one :messageOne="messageone" @brotheroneSaid="pMessageTwo($event)"></brother-one>
      <brother-two :messageTwo="messagetwo" @brothertwoSaid="pMessageOne($event)"></brother-two>
	</div>
</template>
<script>
	import BrotherOne from './BrotherOne';
	import BrotherTwo fro './BrotherTwo';
	export default {
		name: 'Parent',
		data() {
			messageone: '',
			messagetwo: ''
		},
		components: {
			BrotherOne,
			BrotherTwo
		},
		methods: {
			pMessageOne(message) {
				this.messageone = message
			},
			pMessageTwo(message) {
				this.messagetwo = message
			},
		}
	}
</script>

<!-- 子组件 -->
<template>
	<div class="container">
		<button @click="messageTwo">给BrotherTwo发消息</button>
		<p>{{ messageOne }}</p>
	</div>
</template>
<script>
	export default {
		name: 'BrotherOne',
		props: ['messageOne'],
		methods: {
			messageTwo() {
				this.$emit('brotheroneSaid', '来自BrotherOne的消息')
			}
		}
	}
</script>

<template>
	<div class="container">
		<button @click="messageOne">给BrotherOne发消息</button>
		<p>{{ messageTwo }}</p>
	</div>
</template>
<script>
	export default {
		name: 'BrotherTwo',
		props: ['messageTwo'],
		methods: {
			messageOne() {
				this.$emit('brothertwoSaid', '来自BrotherTwo的消息')
			}
		}
	}
</script>

其实这种方式实现的原理类似与前面所说的子组件向父组件使用emit通信的方式,先对某个子组件设置一个click事件:

<button @click="messageTwo">给BrotherTwo发消息</button>

当点击按钮时会触发messageTwo方法,在这个方法中用emit触发父组件监听的brotheroneSaid事件,一旦触发了父组件的brotheroneSaid事件,那么就会触发其绑定的pMessageTwo方法

2.通过EventBus实现

前面的那个方法在子组件不多的情况下可用,但是如果组件数量较多,那么代码就会很复杂,也容易绕晕,所以我们需要引入EventBus
我们创建一个新文件eventbus.js,内容如下:

import Vue from 'vue';
export default new Vue();

可以看出实际上,它就是创建一个全新的vue实例。
然后我们在组件中引入同一个文件,假如我们要在生命周期钩子中做监听:

<!-- 组件1 -->
import eventbus from 'eventbus.js';
data() {
	message: ''
},
created() {
	eventbus.$on('getMsg', this.getMsg);
},
beforeDestory() {
	eventbus.$off('getMsg', this.getMsg);
},
methods: {
	getMsg(param) {
		this.message = params
	}
}
<!-- 组件2 -->
import eventbus from 'eventbus.js';
methods: {
	doSomething() {
		eventbus.$emit('getMsg', 'demo');
	}
}

实际上,前面提到的兄弟组件间通信的方法都不适合用于数据量很大的操作。更优的方案应该是使用vuex

如果你想让上面的demo在线运行,请点击: https://codesandbox.io/s/yrkk2rx1v
Edit Vue Template



参考

W3plus - Vue组件通讯
W3plus - Vue 2.0学习笔记:事件总线(EventBus)
W3plus - Vue 2.0学习笔记:不同场景下组件间的数据通讯