We all know that Vue is a componentized development mode, componentized advantage lies in relative independence, easy maintenance, reusable. You can think of a project as a collection of components.

Since there are many components in the project, and they are relatively independent, but there must be data transfer interaction between components. Vue gives me more ways to communicate between components.

This article is not intended to detail component communication, but rather to talk about nested component communication using $attrs and $Listeners.

You can imagine a project in which components are related to each other in a variety of ways: parent and child, brother, grandparent (nested).

  1. Parent component: The parent component passes child component data down through props, and the child component sends parent component messages up through events. Alternatively, you can retrieve data and events via the ref attribute, $parent, $children, and so on.
  2. Sibling components: Can communicate via a common parent component as a bridge, global event eventBus or the more complex Vuex.

A picture is worth a thousand words

From the figure above, we can see the common component communication methods. In real development projects, however, it may not be as simple as this. In recent projects, you have encountered nested components, such as “component A” containing “component B” and “component B” containing “component C”.

Then how to communicate between “component A” and “component C” is worth discussing. Should Vuex or other ways be used?

First, Vuex is an excellent state management tool, which is ideal for complex and large systems.

However, if our system is relatively simple, and there is only simple data transfer between “component A” and “component C”, it seems that introducing Vuex is not A good choice and will increase complexity.

However, Vue has added two properties “$Attrs” and “$Listeners” in version 2.4.0 that are a great way to use them for nested component communication. Let’s see what they are and how they can be used.

1.$attrs

Official explanation: Contains feature bindings (except class and style) that are not recognized (and retrieved) as prop in the parent scope. When a component does not declare any prop, all parent-scoped bindings (except class and style) are included, and internal components can be passed in via V-bind =”$attrs” — useful when creating higher-level components.

2.$listeners

Contains v-ON event listeners in the parent scope (without the.native modifier). Internal components can be passed in via V-on =”$Listeners “– useful for creating higher-level components.

$Listeners are the functions of the parent component and the functions of the parent component. $listeners are functions of the parent component and functions of the parent component.

Look at an example in case you don’t understand

//componentA
<template>
  <div class="component-a">
    <component-b :name="name" :tag="tag" :age="age" @click.native="say" @mouseover="sing"></component-b>
  </div>
</template>
<script>
import componentB from "./ComponentB";
export default {
  name: "componentA",
  components: { componentB },
  data() {
    return {
      name: "Elder brother",
      tag: "Handsome",
      age: 18
    };
  },
  methods: {
    say() {},
    sing() {}}}; </script> //componentB <template> <div class="component-b"></div>
</template>
<script>
export default {
  name: "ComponentB",
  props: {
    age: Number
  },
  mounted() {
    console.log(this.$attrs, this.$listeners);
    //{name: "Elder brother", tag: "Handsome"}, {mouseover: ƒ}}}; </script>Copy the code

With these two properties in mind, let’s take a look at how you can use them to pass between grandparent components. Suppose there are three components: Component A contains component B, and component B contains component C.

Based on the above example, we add “Component C” and improve “component B”.

//componentB
<template>
  <div class="component-b">
    <component-c v-bind="$attrs" v-on="$listeners"></component-c>
  </div>
</template>
<script>
import ComponentC from "./ComponentC";
export default {
  name: "ComponentB",
  components: { ComponentC }
};
</script>

//componentC
<template>
  <div class="component-c"></div>
</template>
<script>
export default {
  name: "ComponentC".mounted() {
    console.log(this.$attrs, this.$listeners);
    //{name: "Elder brother", tag: "Handsome", age: 18} {mouseover: ƒ}}}; </script>Copy the code

Note that $listeners use $attrs and $Listeners to communicate data from Component A to component C without introducing additional tools or global properties. If you have a friend in the same situation, use it now.

Another thing to note here is that we are using the non-props feature. Components in Vue that accept non-props attributes render the attributes to native HTML tags.

Such as:

<component-b :name="name" :tag="tag"></component-b>
Copy the code

The rendered result will be

<div class="component-b" name="Elder brother" tag="Handsome"></div>
Copy the code

This is obviously not what we want, as our native tags do not require the “name” and “tag” attributes. So how can it be avoided? As simple as that, you can set inheritAttrs: false in the component’s options.

<script>
import ComponentC from "./ComponentC";
export default {
  inheritAttrs: false,
  name: "ComponentB",
  components: { ComponentC }
};
</script>
Copy the code

These are the listeners of today’s $attrs and $Listeners are welcome to comment on them. If you think this article is interesting, please feel free to like it.