Vue $attrs 与 $listeners的使用
一、$attrs内容
$attrs是什么?
包含了父作用域中不作为 prop 被识别 (且获取) 的 attribute 绑定 (class 和 style 除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过 v-bind=“$attrs” 传入内部组件——在创建高级别的组件时非常有用。
以上内容为,vue官方文档中对$attrs的介绍。一堆修饰限定、名词堆砌,对于新手来说可谓是极其不友好。
其实想要弄清楚$attrs到底是个什么东西,也很简单,先要了解几个概念。
1. attribute: 即html元素中所拥有的属性。
<!-- 如id、title、onclick等,这些都属于attribute -->
<div id="box" title="标题" onclick="fun()"></div>
2. 父组件: 组件化是vue中一个非常重要的概念,vue文件内能引用其他的vue文件作为组件使用,以此达到简化和复用代码的目的。一般我们将被引用的vue组件称为“子组件”,而引用“子组件”的页面,称之为父组件。
<!-- 我是组件Father.vue文件 -->
<div>
<son>我是子组件</son>
</div>
3.父作用域: 理解了上面父组件的概念后,父作用域是什么就呼之欲出了。即父组件模板所在的作用域,即为父作用域,非常浅显易懂有没有。
拿父组件中的代码举例,父作用域的范围就是,Father.vue文件。
结合上方官方文档中的定义与上面的几个概念,我对$attrs做出的理解就是父组件中,未被子组件使用prop接收的那些属性(class 和 style 除外))
$attrs能做些什么?
根据官方文档的说法就是,(父组件中未被子组件接收的值)可以通过 v-bind=“$attrs” 传入内部组件,即传入子组件中的组件。也就是说,只要父组件传递的参数,没有被子组件或是子组件的内部组件,用prop所接收,那么就能实现父组件对任意后代组件进行传递参数的操作。
这是一个非常强大的功能。因为如果不依靠$attrs,来实现父组件对其子组件以外的后代组件,进行传参操作。
我所能想到的就只有vuex、事件总线、或是prop逐级进行接受传递等方法。与 $attrs对比来看,可谓是十分的繁琐、且不优雅。
$attrs使用方式
父组件
<template>
<div>
<div class="father">
<span>父组件</span>
<span>name:{{ name }}</span>
<span>obj:{{ obj }}</span>
</div>
<son :id="id" :name="name" :age="age" :obj="obj"/>
</div>
</template>
<script>
import Son from "./Son";
export default {
name: "Father",
components: {Son},
data() {
return {
id: 001,
name: 'Rise',
age: 66,
obj: {
a: 1,
b: 2
}
}
}
}
</script>
子组件
<template>
<div>
<div class="son">
<span>子组件</span>
</div>
<grandson v-bind="$attrs"/>
</div>
</template>
<script>
import Grandson from "./Grandson";
export default {
name: "Son",
inheritAttrs:false,
components: {Grandson},
data(){
return{}
}
}
</script>
孙组件
<template>
<div class="grandson">
<div>孙组件</div>
<span>name:{{name}}</span>
<span>obj:{{obj}}</span>
</div>
</template>
<script>
export default {
name: "Grandson",
inheritAttrs:false,
props:{
name:{type:String},
obj:{type:Object}
}
}
</script>
$attrs传参注意事项
1. 首先,$attrs所传递的参数为,子组件未接受的参数,所以如果参数在子组件内被使用prop接收过了,那么孙组件将无法在接收到。
2. 可以看到inheritAttrs属性,在上方的代码中出现。inheritAttrs是用来指定,$attrs中的值,是否在当前组件的根节点上显示,如果指定其值为true,则 $attrs中的值会显示在当前组件的dom节点的属性中。
将子组件中的inheritAttrs设置为true,可以看到,除被其接受到的name外,其他父组件传的参数,都显示到了其根节点的属性中。
二、$listeners内容
$listeners是什么?
包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on=“$listeners” 传入内部组件——在创建更高层次的组件时非常有用。
上面是vue官方文档对$listeners的定义,结合上面所提到的 $attrs,是不是感觉两者特别相似。
$attrs内接受的是父组件所传递的属性,而 $listeners内接受的则是父组件中所传递的方法
。
$listeners能做些什么?
根据上方官方文档的说法,它可以通过 v-on=“$listeners” 传入内部组件。即把父组件中的事件监听,并传递到其所有后代。
试问一下,提到子组件向父组件传参,我们最常用方式,是不是用 $emit()方法,来调用父组件中的自定义事件。
而使用 v-on="$listeners" ,只要使用得当,即可优雅的在任何后代元素中,改变父组件的值,可谓是一个十分强大的功能。
$listeners使用方式
父组件
<template>
<div>
<div class="father">
<span>父组件</span>
<span>name:{{ name }}</span>
</div>
<son name="name" @changeName="changeName"/>
</div>
</template>
<script>
import Son from "./Son";
export default {
name: "Father",
components: {Son},
data() {
return {
name: 'Rise'
}
},
methods: {
changeName(name) {
this.name = name
}
}
}
</script>
子组件
<template>
<div>
<div class="son">
<span>子组件</span>>
</div>
<grandson v-on="$listeners"/>
</div>
</template>
<script>
import Grandson from "./Grandson";
export default {
name: "Son",
components: {Grandson}
}
</script>
孙组件
<template>
<div class="grandson">
<div>孙组件</div>
<div>
<button @click="listeners()">$listeners</button>
</div>
</div>
</template>
<script>
export default {
name: "Grandson",
methods:{
listeners(){
this.$emit('changeName','tim')
}
}
}
</script>
结果
孙组件触发changeName前:
孙组件触发changeName后:
$listeners注意事项
1. 与$attrs值被接收后,只能一个后代使用不同。$listeners支持所有后代一起使用,即子组件内使用过后,孙组件及其后代依然能够传递与调用。
结语
$attrs 与 $listeners诞生于vue2.4版本,其出现使得父孙乃至父组件与任意后代间的通信,变得更加的简单与优雅。
作为一个vue领域的学徒,以上为我的一些个人理解,一家之言。如有错误欢迎指正!