Several ways of vue component communication

Posted by fazlionline on Mon, 28 Feb 2022 15:22:19 +0100

preface

In vue, there are no more than three kinds of component relationships:

Components need to communicate. In development, common communication methods include vuex, eventBus, props and emit, $parent and $children. In addition, there are also provide and inject, $attrs and $listeners.

1, vuex

I believe you have used this a lot. Let's briefly review it:

  • State: place the state
  • Mutation: the only place to modify the state. Asynchronous is not supported
  • Action: modify the state by calling the method in the Mutation, and support asynchronous
  • Getter: it can be understood as calculating attributes
  • Module: module. Each module has its own state, mutation, action and getter
    For simple use, I won't repeat here. Let's mention the namespace in module.

If you want your module to have higher encapsulation and reusability, you can make it a module with namespace by adding named: true. When a module is registered, all its getter s, action s and mutation s will be automatically named according to the path of module registration


In this way, we can use it in this way:

2, eventBus

This is called "event bus". Let's briefly see how to use it:

  • initialization
    The first is to initialize an eventBus, which can be bound to vue prototype or window object. It can also be extracted as a module and introduced when necessary. This is directly bound to the vue prototype:

  • Create and delete events
    Create and delete events on required components:

  • Trigger event
    Finally, trigger the event where needed

3, props/emit

Needless to say, this should be the most used for father son communication. Of course, if the sub components are used as a springboard, the communication between grandparents and grandchildren can also be achieved, but it is more troublesome. This is not recommended.

4, $parent/$children

$parent directly accesses the parent instance, while $children returns the instance array. So I usually use $parent with $refs.

5, $attrs/$listeners

These two may be used less. Let's take a look at the introduction on the official website:

How to understand it? Simply put, $attrs receives all the binding attributes except prop, style and class, and $listeners receives all the binding attributes except for being All binding events modified by native. For example:

<template>
  <div>
    <p>Parent component</p>
    <input type="text" v-model="formData.inputValue" />
    <p>Subcomponents</p>
    <Son
      :inputValue="formData.inputValue"
      :otherValue="otherValue"
      @success="success"
      @input.native="handleInput"
      v-bind="$attrs"
      v-on="$listeners"
    ></Son>
  </div>
</template>
<script>
import Son from "./son.vue";
export default {
  components: { Son },
  provide() {
    return {
      father: this.formData,
    };
  },
  data() {
    return {
      formData: {
        inputValue: "123",
      },
      otherValue: 999,
    };
  },
  methods: {
    success(data) {
      console.log(data);
    },
    handleInput() {},
  },
};
</script>

<template>
  <div>
    <input type="text" v-model="inputValue" @change="handleChange" />
  </div>
</template>
<script>
export default {
  props: {
    inputValue: String,
  },
  created() {
    console.log(this.$attrs, "son---$attrs");
    console.log(this.$listeners, "son---$listeners");
  },
  methods: {
    handleChange() {
      this.father.inputValue = this.inputValue;
    },
  },
};
</script>

According to the previous understanding, $attrs should only receive otherValue and $listeners can only receive success events. See the print results below:

The result is indeed the same. In addition, it can also be passed to sub components:

<template>
  <div>
    <input type="text" v-model="inputValue" @change="handleChange" />
    <GrandSon v-bind="$attrs" v-on="$listeners"></GrandSon>
  </div>
</template>
<script>
import GrandSon from "./grandSon.vue";
export default {
  components: { GrandSon },
  props: {
    inputValue: String,
  },
  created() {
    console.log(this.$attrs, "son---$attrs");
    console.log(this.$listeners, "son---$listeners");
  },
  methods: {
    handleChange() {
      this.father.inputValue = this.inputValue;
    },
  },
};
</script>

<template>
  <div>
    <input type="text" v-model="inputValue" @change="handleChange" />
  </div>
</template>
<script>
export default {
  props: {
    inputValue: String,
  },
  created() {
    console.log(this.$attrs, "grandSon---$attrs");
    console.log(this.$listeners, "grandSon---$listeners");
  },
  methods: {
    handleChange() {
      this.father.inputValue = this.inputValue;
    },
  },
};
</script>


In this way, communication between grandparents and grandchildren has also been realized.

6, provide/inject

provide/inject can inject a dependency into all its descendant components in an ancestor component, which can take effect as long as the upstream and downstream relationship is established. The simple understanding is that provide is to inject data and inject is to obtain data. Therefore, provide is used for parent components and inject is used for descendant components. Provide should be an object or a function that returns an object, and inject should be a string array or an object. The official website mentioned the following sentence:

Tip: provide and inject bindings are not responsive. This is deliberate. However, if you pass in a listener object, the property of the object is still responsive.

How do you understand this sentence? Literally, if the data you want to transfer upstream and downstream is responsive, you should transfer it in the form of object. First try to transfer it in the form of basic data type. See the following example:
Parent component:

<template>
  <div>
    <p>Parent component</p>
    <input type="text" v-model="inputValue" />
    <p>Subcomponents</p>
    <Son></Son>
    <p>Sun component</p>
    <GrandSon></GrandSon>
  </div>
</template>
<script>
import Son from "./son.vue";
import GrandSon from "./grandSon.vue";
export default {
  components: { Son, GrandSon },
  provide() {
    return {
      father: this.inputValue,
    };
  },
  data() {
    return {
      inputValue: "123",
    };
  },
};
</script>

Sub components:

<template>
  <div>
    <input type="text" v-model="inputValue" @change="handleChange" />
  </div>
</template>
<script>
export default {
  inject: ["father"],
  data() {
    return {
      inputValue: "",
    };
  },
  watch: {
    father(val) {
      console.log(val, "val");
      this.inputValue = val;
    },
  },
  created() {
    console.log(this, "this");
  },
  methods: {
    handleChange() {
      this.father.inputValue = this.inputValue;
    },
  },
};
</script>

Print this in the subcomponent:

You can see that the inputValue value of the parent component is injected into the child component. But I can't monitor this father.

Then, we change to inject in the form of object:

<template>
  <div>
    <p>Parent component</p>
    <input type="text" v-model="formData.inputValue" />
    <p>Subcomponents</p>
    <Son></Son>
    <p>Sun component</p>
    <GrandSon></GrandSon>
  </div>
</template>
<script>
import Son from "./son.vue";
import GrandSon from "./grandSon.vue";
export default {
  components: { Son, GrandSon },
  provide() {
    return {
      father: this.formData,
    };
  },
  data() {
    return {
      formData: {
        inputValue: "123",
      },
    };
  },
};
</script>

<template>
  <div>
    <input type="text" v-model="inputValue" @change="handleChange" />
  </div>
</template>
<script>
export default {
  inject: ["father"],
  data() {
    return {
      inputValue: "",
    };
  },
  watch: {
    'father.inputValue'(val){
      console.log(val, "val");
      this.inputValue = val;
    },
  },
  created() {
    console.log(this, "this");
  },
  methods: {
    handleChange() {
      this.father.inputValue = this.inputValue;
    },
  },
};
</script>

At this time, let's take a look at the printed this and its effect:


In this way, the data response can be realized. One thing to note here is that if this of the entire parent component is injected into the descendant component in the parent component, the injected object cannot be monitored through deep listening in the descendant component, and a stack overflow error will be reported. So I use this here Injection in the form of formdata. In this way, in the descendant component, you can use 'father Inputvalue 'can also be used for listening:

father: {
      handler(val) {
        console.log(val);
      },
      deep: true,
    },

As for why this problem is caused, let's first look at the implementation of deep listening:

What does this comment mean? It is simply understood that vue traverses each attribute of an object recursively and collects the attributes of the object for listening. As we all know, recursion is easy to cause stack overflow, and it is not difficult to understand why this object causes stack overflow (too many, and nested layer by layer).
The above are several ways of Vue component communication. If you want to talk about it, the browser cache can also be used as a means...

Topics: Javascript Front-end Vue Vue.js