在组件的五种通信方式中,父子组件之间的通信占了四种,而兄弟组件间的通信占了一种。下面我们详细给出这五种通信方式
一. 父子组件之间的四种通信方式
- props和$emit()
这是最常被使用的一种父子组件通信方式,而且常用于单层的父子组件间传值。子组件使用props:[‘父组件自定义的属性名’]接收父组件传给自身的数据,而子组件要传值给父组件则使用$emit(‘父组件自定义的事件名’,传输的具体值)。
具体来说,父组件向子组件传值分三步走。首先,父组件在使用子组件的时候绑定自定义属性传值给子组件;接着,子组件使用props接收父组件传来的值;最后,子组件使用自己已经接收后的值。而子组件向父组件传值也分三步骤。首先,父组件在使用子组件时绑定自定义事件;其次,子组件通过触发原生事件,在原生事件对应的函数中使用$emit(‘父组件绑定的自定义事件名’, 需传输给父组件的值)来触发自定义事件;最后,父组件在自身内部使用子组件传输给父组件的值。 详细代码如下所示:<!DOCTYPE html> <html> <head> <meta charset='uft-8'> <title>Vue中父子组件之间使用props以及$emit进行通信</title> </head> <body> <div id='app'></div> <script type='text/javascript' src='node_modules/vue/dist/vue.js'></script> <script type='text/javascript'> // 定义父组件 Vue.component('Parent',{ data:function() { return{ msg:'我是父组件给子组件的参数' } }, //父组件传值给子组件第1步:父组件绑定自定义属性,giveChildProp //子组件传值给父组件第1步:父组件绑定自定义事件,getChildMessage template:` <div> 我是全局组件父组件 <Child :giveChildProp='msg' @getChildMessage='getChildMessage'/> <p>我位于父元素当中:{{msg}}</p> </div> `, methods:{ getChildMessage:function(val) { this.msg = val console.log(val) } } }) // 定义子组件 Vue.component('Child',{ //父组件传值给子组件第3步:子组件任意使用已经在props中申明过的属性 // v-model是双向绑定,但是只是针对单个组件中的双向绑定,input中的内容修改后并不会影响父元素中msg对应的值,那么如何将子元素中的数据传回给父元素呢?子组件中应用$emit(父元素中自定义的事件名,需要传输的数据) data:function() { return { childMsg:'我是来自child的数据' } }, template:` <div> 我是全局组件子组件 <input type='text' :value='giveChildProp' @input='transferMessageToParent'/> <p>我位于子元素当中:{{giveChildProp}}</p> </div> `, //父组件传值给子组件第2步:子组件使用props申明父组件绑定的属性 props:['giveChildProp'], methods:{ transferMessageToParent:function(e) { this.$emit('getChildMessage',e.target.value) } } }) var vm = new Vue({ el:'#app', data:function() { return { } }, template:`<Parent/>` }) </script> </body>
当在子组件的输入框中输入xx时,则触发了原生事件input,事件对应的transferMessageToParent函数使用$emit将input中最新的值传给父组件中绑定的自定义事件getChildMessage。在事件getChildMessage对应的函数getChildMessage中,修改父组件的msg的值,因此页面中但凡使用了msg的地方都更新为最新的input值,即’我是父组件传给子组件的参数xx’
- v-bind=’$attrs’和v-on=’$listeners’
常用于多层父子组件间数据的层层传递,即祖辈组件和孙辈组件间的数据传递。当组件之间存在多层嵌套之时,如App组件嵌套A,A组件则嵌套B,B组件则嵌套C,若是采用以前的父子组件传值方式props和$emit,则相当复杂,所以我们引入$attrs和$listeners。
具体来说,利用$attrs让父组件将值层层传递给子组件。 需要说明的有三点:(1)在子组件中使用$attrs,可以获取父组件传给自己的静态属性值和动态属性值,动态属性值就是自己通过:绑定的自定义属性。若是属性在子组件的props中接收了,则不再出现在$attrs中;(2)App组件使用子组件A时绑定自定义属性,子组件则可以获取A被使用时的全部属性,通过$attrs,要获取某一具体属性则通过$attrs.属性名;(3)因为A组件要将信息传给B组件,则在使用B组件的时候,通过v-bind=’$attrs’;B组件还需要传给C组件,在使用C组件之时,定义v-bind=’$attrs’。因此,C组件中能直接使用$attrs,即{{$attrs}}以获取到App组件给自己的全部数据。
同时,利用$listeners让子组件将值层层传递给父组件。App组件使用A组件的时候绑定自定义事件,自定义事件对应的函数定义在methods中; C组件中在视图层绑定原生事件,原生事件对应的函数定义在methods中, 函数中的具体内容为this.$emit(‘App中自定义的事件名’需传给App父元素的数据)。同时,为了将数据传给App, 在使用B组件和C组件时,应该在组件上绑定v-on=’$listeners’;
具体代码如下
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
<title>Vue多层父子组件之间使用v-bind='\$attrs'和v-on='\$listeners'进行通信</title>
</head>
<body>
<div id='app'></div>
<script type='text/javascript' src='node_modules/vue/dist/vue.js'></script>
<script type='text/javascript'>
let C = {
data:function() {
return {
cMsg:'我是C组件传给App组件的数据'
}
},
template:`
<div class='testC'>
<div @click='clickHandler'>
{{$attrs.appData}}
</div>
</div>
`,
methods:{
clickHandler:function() {
this.$emit('textClick',this.cMsg)
},
},
created:function() {
console.log('我从C上输出:'+ this.$attrs.appData)
}
}
let B = {
template: `
<div class=' testB'>
<C v-bind='$attrs' v-on='$listeners'></C>
</div>
`,
components:{
C
},
created:function() {
console.log('我从B上输出:'+ this.$attrs.appData)
}
}
let A = {
template:`
<div class='testA' >
<h2>来自A组件:{{$attrs}}</h2>
<B v-bind='$attrs' v-on='$listeners'></B>
</div>
`,
components:{
B
},
created:function() {
console.log('我从A上输出:'+ this.$attrs.appData)
}
}
let App = {
data:function() {
return {
appData:'我是App组件传给C组件的数据',
newAppData:'我是第二条从App组件传给C组件的数据'
}
},
components:{
A
},
template:`
<div class='testApp'>
<A name='IamA' :appData='appData' @textClick='textClick'></A>
</div>
`,
created:function() {
console.log('我从App上输出:'+this.appData)
},
methods:{
textClick:function(value) {
console.log(value)
}
}
}
let vm = new Vue({
el:'#app',
data:function() {
return {
}
},
components:{
App
},
template:`
<App class='testVue'/>
`
})
</script>
</body>
</html>
App是否将数据成功传递给子组件的情况图:
点击C组件触发clickHandler方法后成功将值从组件C传到组件App后由组件App中输出的内容:
- provide:{}和inject:[’’]的使用
接下来要讲的这种组件间通信方式既适用于父子组件间通信又适用于祖辈和孙辈组件间的通信。使用方式也较为简单。父组件或祖辈组件向子组件或孙辈组件传值时,只需在指明provide:{msg:’ xx’}。 其中msg就是要传递的值。而使用了provide的组件的子组件或者孙辈组件只需要使用inject:[‘msg’] 将msg注入到自身组件中即可获取到了该数据信息。
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
<title> provide:{}和inject:[''] 实现父子组件以及祖辈和孙辈组件之间的通信</title>
</head>
<body>
<div id='app'></div>
<script type='text/javascript' src='node_modules/vue/dist/vue.js'></script>
<script type='text/javascript'>
Vue.component('Grandson',{
data:function() {
return {
msg:'我是Grandson'
}
},
template:`
<div>
<p>{{msg}}</p>
<p>{{toChild}}</p>
</div>
`,
inject:['toChild']
})
Vue.component('Child',{
data:function() {
return {
msg:'我是Child'
}
},
template:`
<div>
<p>{{msg}}</p>
<p>{{toChild}}</p>
</div>
`,
inject:['toChild']
})
let App = {
template:`
<div>
<Child/>
</div>
`,
provide:{
toChild:'我是组件App的数据'
}
}
let vm = new Vue({
el:'#app',
data:function() {
return {
}
},
components:{
App
},
template:`
<App/>
`
})
</script>
</body>
</html>
由结果可知,provide:{}和inject:[’’] 实现父子组件以及祖辈和孙辈组件之间的通信
- $children和$parent
使用$children可以获得当前元素的全部直接子元素的集合;使用$parent可以获得当前元素直接父元素。当前元素利用$children获得某一子组件的数据this.$children[0].msg(假设获取第一个子组件的msgBtn数据),当前元素利用$parent获得父元素的数据this.$parent.msg (假设获取父元素的msgApp 数据)。见下面详细代码。
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
<title>Vue组件中通信父子组件间的通信之$children和$parent</title>
</head>
<body>
<div id='app'></div>
<script type='text/javascript' src='node_modules/vue/dist/vue.js'></script>
<script type='text/javascript'>
Vue.component('Btn',{
data:function() {
return{
msgBtn:'我是Btn的数据',
msg:''
}
},
template:`
<button>点我:{{msg}}</button>
`,
created:function() {
// 成功获取了父组件App的中的数据属性
this.msg = this.$parent.msgApp
}
})
Vue.component('Input',{
data:function() {
return {
msgInput:'我是Input的数据'
}
},
template:`
<input type='text' v-model='msgInput'/>
`
})
let App = {
data:function() {
return {
msgApp:'我是App的数据',
msgGetBtn:'',
msgGetInput:''
}
},
template:`
<div>
<Btn/>
<Input/>
<p>{{msgGetBtn}}</p>
<p>{{msgGetInput}}</p>
</div>
`,
mounted:function() {
// 通过$children的方式成功获取了直接子组件,并且再通过.的使用又可以获取子组件中的数据属性
console.log(this.$children)
this.msgGetBtn = this.$children[0].msgBtn
this.msgGetInput = this.$children[1].msgInput
}
}
let vm = new Vue({
el:'#app',
data:function() {
return {
}
},
components:{
App
},
template:`
<App/>
`
})
</script>
</body>
</html>
结果如下:
二. 兄弟组件之间的通信方式
兄弟组件之间实现通信则是采用中央事件总线。总的来说分为三步走,第一,定义一个命名为bus的实例化Vue对象,该对象充当两兄弟之间互传消息的中介;第二,接收数据的兄弟组件在自身的mounted钩子函数中执行bus.$on(接收数据的方法名称,(另外一组件传来的数据msg)=> {…})以实现数据的接收;第三,发送数据的组件通过事件触发自定义方法,而自定义方法中则编写bus.$emit(兄弟组件已定义的接收数据的方法,传送的数据msg)以实现数据的发送。详细代码如下。场景为App组件下包含brother1组件和brother2组件,当前需要利用中央事件总线实现组件brother1向组件brother2传值
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
<title>Vue中兄弟组件通信之中央事件总线 bus.$emit以及bus.$on的使用</title>
</head>
<body>
<div id='app'></div>
<script type='text/javascript' src='node_modules/vue/dist/vue.js'></script>
<script type='text/javascript'>
let bus = new Vue()
Vue.component('brother1',{
data:function() {
return {
data1:'我是brother1的数据'
}
},
template:`
<input type='text' v-model='data1' @input='changeHandler'/>
`,
methods:{
changeHandler:function() {
bus.$emit('getMessage', this.data1)
}
}
})
Vue.component('brother2',{
data:function() {
return {
data2:''
}
},
template:`
<div>
{{data2}}
</div>
`,
mounted:function() {
bus.$on('getMessage',(msg) => {
this.data2 = msg
})
}
})
let App = {
template:`
<div>
<brother1/>
<brother2/>
</div>
`
}
let vm = new Vue({
el:"#app",
data:function() {
return {
}
},
components:{
App
},
template:`
<App/>
`,
})
</script>
</body>
</html>
执行结果:
未触发事件状态:
input中输入数据后的结果: