$attrs:
官方文档介绍:包含了父作用域不作为prop被识别(且获取)的attribute绑定(class和style除外)。当一个组件没有生命任何prop时,这里会包含所有父作用域的绑定(class和style除外),并且可以通过v-bind="$attrs"传入内部组件–在创建高级别的组件时非常有用。
通俗地来说就是如果从父组件传过来的值,没有在子组件中被接收,那么这些值就会被存在$attrs对象中。
下面这个例子中有Father、Son、GrandSon三个组件。
//Father.vue
<template>
<div class="father">
<span>父亲</span>
<Son :message="message" :age="18"></Son>
</div>
</template>
<script>
import Son from "../components/Son.vue";
export default {
data() {
return {
message: "父组件传过来的值",
age: 18,
};
},
components: {
Son,
},
};
</script>
<style>
</style>
//Son.vue
<template>
<div class="son">
<span>儿子</span>
<GrandSon v-bind="$attrs"></GrandSon>
</div>
</template>
<script>
import GrandSon from "../components/GrandSon.vue";
export default {
components: {
GrandSon,
},
created() {
console.log("son", this.$attrs);
},
};
//GrandSon.vue
<template>
<div class="grandson">
<span>孙子</span>
<div>{{ $attrs.message }}</div>
</div>
</template>
<script>
export default {
created() {
console.log("grandson", this.$attrs);
},
};
</script>
<style>
</style>
</script>
<style>
</style>
可以看到无论是在Son组件中还是GrandSon组件中都可以拿到$attrs中的值。
当我们对Son组件进行改变时,
//Son.vue
<template>
<div class="son">
<span>儿子</span>
<GrandSon v-bind="$attrs"></GrandSon>
</div>
</template>
<script>
import GrandSon from "../components/GrandSon.vue";
export default {
components: {
GrandSon,
},
props: {
message: String,
},
created() {
console.log("son", this.$attrs);
},
};
</script>
<style>
</style>
当我们在Son组件中使用props去接收GrandSon发送过来中的值的时候,该值就不会被带到$attrs对象中去,没有被props接收的值还是会被带到$attrs对象中去。
$listeners:
包含了父作用域中的(不包含.native修饰器的)v-on事件监听器。它可以通过v-on="$listeners"传入内部组件–在创建更高层次的组件时非常有用。
通俗地来说就是可以从孙子组件发送事件到父子组件中。
//Father.vue
<template>
<div class="father">
<span>父亲</span>
<Son @event="event"></Son>
</div>
</template>
<script>
import Son from "../components/Son.vue";
export default {
components: {
Son,
},
methods: {
event() {
console.log("从GrandSon组件发送过来的数据");
},
},
};
</script>
<style>
</style>
//Son.vue
<template>
<div class="son">
<span>儿子</span>
<GrandSon v-on="$listeners"></GrandSon>
</div>
</template>
<script>
import GrandSon from "../components/GrandSon.vue";
export default {
components: {
GrandSon,
},
};
</script>
<style>
</style>
//GrandSon.vue
<template>
<div class="grandson">
<span>孙子</span>
<button @click="click">发送事件</button>
</div>
</template>
<script>
export default {
methods: {
click() {
this.$emit("event");
},
},
};
</script>
<style>
</style>
普通的emit只能从子组件传到父组件,通过$listeners,我们可以实现从孙子组件把事件传到父子组件,不需要通过emit逐层传递。
$attrs和$listeners主要用来父孙组件之间传值,有的同学说也可以用provide和inject来实现父孙组件之间传值,但需要注意的是provide和inject只能从父组件向孙组件传值,不能从孙组件向父组件传值,这种情况就需要使用$attrs和$listeners来实现了。