前言
vue通信手段有很多种,props/emit
、vuex
、event bus
、provide/inject
等。还有一种通信方式,那就是 $attrs
和 $listeners
,之前早就听说这两个api,趁着有空来补补。这种方式挺优雅,使用起来也不赖。下面例子都会通过父、子、孙子,三者的关系来说明使用方式。
$attrs
官方解释:
包含了父作用域中不作为 prop
被识别 (且获取) 的特性绑定 (class
和 style
除外)。当一个组件没有声明任何 prop
时,这里会包含所有父作用域的绑定 (class
和 style
除外),并且可以通过 v-bind="$attrs"
传入内部组件——在创建高级别的组件时非常有用。
我的理解:
接收除了
props
声明外的所有绑定属性(class、style除外)
图解:
由于child.vue 在 props
中声明了 name
属性,$attrs
中只有age
、gender
两个属性,输出结果为:
{ age: "20", gender: "man" }
上面打印结果为:
{height: '80', name: 'joe', gender: 'man'}//会把child页面的值,一起传过来
注:但若是child.vue页面有个prop:[“name”],则name不会传到grandson.vue页面中去,即:
{height: '80', gender: 'man'}
,如下:
另外可以在 grandson.vue 上通过 v-bind="$attrs"
,可以将属性继续向下传递,让 grandson.vue 也能访问到父组件的属性,这在传递多个属性时会显得很便捷,而不用一条条的进行绑定。
如果想要添加其他属性,可继续绑定属性。但要注意的是,继续绑定的属性和 $attrs
中的属性有重复时,继续绑定的属性优先级会更高。
打印结果为:
{height: '80', name: 'xth', gender: 'man'}//会把child页面的值,一起传过来,同时会以继续绑定的属性为最高优先级
$listeners
官方解释:
包含了父作用域中的 (不含 .native
修饰器的) v-on
事件监听器。它可以通过 v-on="$listeners"
传入内部组件——在创建更高层次的组件时非常有用。
我的理解:
接收除了带有
.native
事件修饰符的所有事件监听器
图解:
parent.vue 中对 child.vue 绑定了两个事件,带有.native
的 click
事件和一个自定义事件,所以在 child.vue 中,输出 $listeners
的结果为:
{ customEvent: fn }
同 attrs
属性一样,可以通过 v-on="$listeners"
,将事件监听器继续向下传递,让 grandson.vue 访问到事件,且可以使用 $emit
触发 parent.vue 的函数。
如果想要添加其他事件监听器,可继续绑定事件。但要注意的是,继续绑定的事件和 $listeners
中的事件有重复时,不会被覆盖。当 grandson.vue 触发 customEvent
时,child.vue 和 parent.vue 的事件都会被触发,触发顺序类似于冒泡,先到 child.vue 再到 parent.vue。
以上转自:Vue - 组件通信之$attrs、$listeners
代码示例
1.$attrs 示例
<div id="app">
根组件
<hr>
<father :msg="msg" :rootName="name" :rootDetail="infor"></father>
</div>
<template id="father">
<div>父组件---{{msg}}
<hr>
<son v-on="$listeners" v-bind="$attrs"></son>
</div>
</template>
<template id="son">
<div >子组件
</div>
</template>
<script>
// 子组件
let son = {
template: '#son',
created() {
console.log(this.$attrs, "子组件,接受root的非prop的数据")
},
}
// 父组件
let father = {
template: '#father',
components: {
son
},
props: ["msg"],
created() {
console.log(this.$attrs, "父组件,接受root的非prop的数据")
},
}
let vm = new Vue({
el: '#app',
components: {
father
},
data() {
return {
msg: "根结点的数据",
name: "root",
infor: {
name: "xth",
age: 18,
address: "app根部"
}
}
},
})
</script>
2.$listeners 示例
<div id="app">
根组件
<hr>
<father @cli1="click1" @cli2="click2" @cli3.native="click3"></father>
</div>
<template id="father">
<div v-on="$listeners">父组件
<button @click="$listeners.cli1">获取root方法cli1</button><br>
<hr>
<son v-on="$listeners"></son>
</div>
</template>
<template id="son">
<div >子组件
<button @click="$listeners.cli2">获取root方法cli2</button>
</div>
</template>
<script>
// 子组件
let son = {
template: '#son',
created() {
console.log(this.$listeners, "子组件") // 包含父级所有绑定的方法
},
}
// 父组件
let father = {
template: '#father',
components: {
son
},
created() {
console.log(this.$listeners, "父组件,接受父级所有绑定的方法") // 包含父级所有绑定的方法
},
}
let vm = new Vue({
el: '#app',
components: {
father
},
methods: {
click1() {
console.log('点击事件cli1')
},
click2() {
console.log('点击事件cli2')
},
},
})
</script>
3.$attrs 、$listeners 、 prop 、 $emit 示例
<div id="app">
根组件
<hr>
<father :msg="msg" :rootName="name" :rootDetail="infor" @cli1="click1" @cli2="click2" @cli3.native="click3"></father>
</div>
<template id="father">
<div v-on="$listeners">父组件---{{msg}}
<button @click="$listeners.cli1">获取root方法cli1</button><br>
<button @click="getRootMethod">获取root方法</button>
<hr>
<son v-on="$listeners" v-bind="$attrs" @fathermethod="fatherMethod"></son>
</div>
</template>
<template id="son">
<div >子组件
<button @click="$listeners.cli2">获取root方法cli2</button>
<button @click="getFatherMethod">获取father方法</button>
</div>
</template>
<script>
// 子组件
let son = {
template: '#son',
created() {
console.log(this.$attrs, "子组件,接受root的非prop的数据")
console.log(this.$listeners, "子组件") // 包含父级所有绑定的方法
},
methods: {
getFatherMethod() {
this.$emit('fathermethod');
}
}
}
// 父组件
let father = {
template: '#father',
components: {
son
},
props: ["msg"],
created() {
console.log(this.$attrs, "父组件,接受root的非prop的数据")
console.log(this.$listeners, "父组件,接受父级所有绑定的方法") // 包含父级所有绑定的方法
},
methods: {
getRootMethod() {
this.$parent.rootMethod();
},
fatherMethod() {
console.log('子组件的getFatherMethod方法点了我');
},
}
}
let vm = new Vue({
el: '#app',
components: {
father
},
data() {
return {
msg: "根结点的数据",
name: "root",
infor: {
name: "xth",
age: 18,
address: "app根部"
}
}
},
methods: {
click1() {
console.log('点击事件cli1')
},
click2() {
console.log('点击事件cli2')
},
click3() {
console.log('点击事件cli3')
},
rootMethod() {
console.log('父组件的getRootMethod方法点了我');
},
},
})
</script>