$ attrs与$ listeners的主要应用是实现多层嵌套传递。
组件A与组件B通信一般都会使用组件B中转,即A传递给B,B再给C,但是如果A到C组件之间嵌套的组件过多,需要传递的事件和属性较多,会导致代码繁琐,代码维护困难。在vue2.4中,为了解决该需求,引入了 a t t r s 和 attrs和 attrs和listeners,新增了inheritAttrs选项。
官方定义:
包含了父作用域中不作为prop 被识别 (且获取) 的特性绑定 (class和 style除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class和 style除外),并且可以通过 v-bind="$attrs" 传入内部组件——在创建高级别的组件时非常有用。
(好抽象一脸懵逼。。。)
a t t r s 只 代 表 的 是 那 些 没 有 被 声 明 为 p r o p s 的 属 性 , 如 果 某 个 p r o p 被 子 组 件 中 声 明 了 ( 就 是 这 个 属 性 已 经 在 子 组 件 的 p r o p s 中 了 ) , 在 子 组 件 中 的 attrs只代表的是那些没有被声明为props的属性,如果某个prop被子组件中声明了(就是这个属性已经在子组件的props中了),在子组件中的 attrs只代表的是那些没有被声明为props的属性,如果某个prop被子组件中声明了(就是这个属性已经在子组件的props中了),在子组件中的attr会把声明的prop剔除。
个人理解: 一个组件在父组件中被引用,$attrs就是组件标签上的静态属性值(attr)和动态属性值(:attr)的对象集合,这个集合不包含class, style和事件属性
// 父组件
<child-com class="com" name="attr" :foo="foo" :boo="boo" :coo="coo" :doo="doo" @click="test"></child-com>
...
data() {
return {
foo: 'Hello World!',
boo: 'Hello Javascript!',
coo: 'Hello Vue',
doo: 'Last'
}
},
...
// child-com.vue
export default {
name: 'childCom',
components: {
childCom2
},
created() {
console.log(this.$attrs) // {name: "attr", foo: "Hello World!", boo: "Hello Javascript!", coo: "Hello Vue", doo: "Last"}
}
}
如果子组件声明了 p r o p , prop, prop,attrs中与$props相同的属性会被移除
// child-com.vue
export default {
name: 'childCom',
props: ['foo'], // foo被声明
components: {
childCom2
},
created() {
console.log(this.$attrs) // {name: "attr", boo: "Hello Javascript!", coo: "Hello Vue", doo: "Last"}
}
}
如果childCom里的子组件还用到foo,可以继续将foo传给子组件
//如果child的子组件c还有一个是其父组件(child组件)的prop属性,
//不是在c组件绑定的,那么c组件的$attrs组件就有这个组件,即
<template>
<div>
<p>foo: {
{
foo}} </p>
<child-com2 :foo="foo" v-bind="$attrs"></child-com2>
</div>
</template>
<script>
const childCom2 = () => import('./childCom2.vue')
export default {
name: 'childCom1',
props: ['foo'], // foo作为props属性绑定
inheritAttrs: false,
components: {
childCom2
},
created() {
console.log(this.$attrs) // {name: "attr", boo: "Hello Javascript!", coo: "Hello Vue", doo: "Last"}
}
}
</script>
//childCom2.vue
created() {
console.log(this.$attrs) // {foo: "Hello World!", name: "attr", boo: "Hello Javascript!", coo: "Hello Vue", doo: "Last"}
}
inheritAttrs要设置成false还是true(默认)
如果inheritAttrs设置成false那么在html里的标签那些属性就不会显示
如果inheritAttrs设置成true那么在html里的标签那些属性就会显示(每一层都有)
props必须在组件中注册了props才能用拿到值,所以在嵌套层级比较深的组件中$attrs拿值更加便捷
包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过v-on=”$listeners”传入内部组件。
归纳起来也是两点:
$ listeners是组件的内置属性,它的值是父组件(不含.native修饰器的) v-on事件监听器。
组件可以通 过在自己的子组件上使用v-on=”$ listeners”,进一步把值传给自己的子组件。如果子组件已经绑定$ listener中同名的监听器,则两个监听器函数会以冒泡的方式先后执行。
父组件:
<template>
<div class="test">
<child v-bind="{name, sex, age}" v-on="{changeName,changeAge}"></child>
</div>
</template>
<script>
import child from './child'
export default {
data() {
return {
name: '张三',
sex: '男',
age: 11,
}
},
components: {
child
},
methods: {
changeName(name) {
this.name = name
},
changeAge(age) {
this.age = age
}
}
}
</script>
子组件child.vue
<template>
<div class="child">
child组件的$attrs {
{
$attrs}}
<child-child v-bind="$attrs" v-on="$listeners" @showAttrs="showAttrs"></child-child>
</div>
</template>
<script>
import childChild from './child-child'
export default {
name: "child",
props: ['name'],
inheritAttrs: false,
created() {
console.log('child', this.$listeners)
},
components: {
childChild
},
methods: {
showAttrs() {
console.log(this.$attrs)
}
}
}
</script>
孙子组件:child-child.vue
<template>
<div class="child-child">
child-child组件的$attrs {
{
$attrs}}
</div>
</template>
<script>
export default {
name: "child-child",
inheritAttrs: false,
created() {
console.log('child-child',this.$listeners)
}
}
</script>