Let’s first look at this diagram, which represents a multilevel nested association of components
Why use it$attrs
和 $listeners
Let’s think about A case where component A and component C communicate. How many solutions can we have?
- Most people would think of using Vuex for data communication, but if you have a small project with very little shared state among multiple components and very little global data communication, then using Vuex for this functionality is a bit of a stretch
- We can use the component B to do the communication station, when the component needs to send data to A component C, component by props to transmit data to the component B, and then the component B use props to component C, this is A kind of solution, but if the nested component is overmuch, can cause code redundancy and trival, maintenance is more difficult, And if component C also passes data to component A, passing it up layer by layer, it becomes even more troublesome
- A custom Vue data bus is suitable for components to transfer data across levels, but the disadvantage is that when multiple people collaborate, the code is less maintainable and less readable
- There is another solution, provide inject, but this method is officially not recommended, because this method is really difficult to control, for example, I provide this in the root component, grandchild component uses a variable in this, then it is difficult to track the source of the variable. And you don’t know which component in the project uses this variable or whether it’s changed in other components, so this API is used by very few people in the project, but a lot of people use it to write components
In many development cases, we just want to pass the data of component A to component C. If we use props to communicate with component C, this can be done, but the code is not readable and difficult to maintain.
Therefore, $listeners and $attrs have emerged
$attrs
和 $listeners
The use of the
In Vue2.4, $listeners and $attrs were introduced to address this requirement, and the inheritAttrs option was added. Prior to version 2.4, properties in the parent scope that are not recognized as props by default will be “rolled back” and applied to the root element of the child component as a normal HTML feature. Take the following examples
Parent component code:
<template> <div> <child-dom :foo="foo" :bar="bar"></child-dom> </div> </template> <script> import ChildDom from ".. /components/attrs/ChildDom.vue"; export default { components: { ChildDom, }, data() { return { foo: "foo", bar: "bar", }; }}; </script>Copy the code
The code for the child component:
<template>
<div>
<p>foo:{{ foo }}</p>
</div>
</template>
<script>
export default {
props: ["foo"],
};
</script>
Copy the code
Let’s first look at the dom structure printed by the console when we write this:
Add inheritAttrs in 2.4. If you set inheritAttrs to true by default and set inheritAttrs to false by default, these actions will be disabled. However, these features can be put into effect through the instance attribute $attrs, which can be v-bind to non-root elements of the child component.
Modify the code for the child component:
<template>
<div>
<p>foo:{{ foo }}</p>
<p>attrs: {{ $attrs }}</p>
<dom-child v-bind="$attrs"></dom-child>
</div>
</template>
<script>
import DomChild from "./DomChild.vue";
export default {
props: ["foo"],
inheritAttrs: false,
components: {
DomChild,
},
};
</script>
Copy the code
Then add a grandchild component
<template>
<div>
<p>bar:{{ bar }}</p>
</div>
</template>
<script>
export default {
props: ["bar"],
};
</script>
Copy the code
The following information is displayed:
The inheritAttrs attribute uses concise code to pass data from component A to component C using $attrs.
So let’s see how does component C pass to component A?
Note $Listeners have been added to Vue2.4. We are attaching V-on = “$Listeners” to component B and listening for events triggered by component C on component A. I can pass the data from component C to component A.
Modify the parent component’s code:
<template> <div> <child-dom :foo="foo" :bar="bar" @upFoo="update"></child-dom> </div> </template> <script> import ChildDom from ".. /components/attrs/ChildDom.vue"; export default { components: { ChildDom, }, data() { return { foo: "foo", bar: "bar", }; }, methods: { update(val) { this.foo = val; console.log("update success"); ,}}}; </script>Copy the code
Sub-component code:
<template>
<div>
<p>foo:{{ foo }}</p>
<p>attrs: {{ $attrs }}</p>
<dom-child v-bind="$attrs" v-on="$listeners"></dom-child>
</div>
</template>
<script>
import DomChild from "./DomChild.vue";
export default {
props: ["foo"],
inheritAttrs: false,
components: {
DomChild,
},
};
</script>
Copy the code
Sun component code:
<template> <div> <p>bar:{{bar}}</p> < button@click ="startUpFoo"> I want to update foo</button> </div> </template> <script> export default { props: ["bar"], methods: { startUpFoo() { this.$emit("upFoo", "foooooooooooo"); console.log("startUpFoo"); ,}}}; </script>Copy the code
Running results:
Now we know what $attrs, $listerners, and inheritAttrs are for