Communication between components mainly exists in the following three situations:

  • Between parent and child components (index-A, index-B…)
  • Between sibling components (A-B)
  • Intergenerational relationship between components (index-c, index-d)

So how exactly do they communicate with each other

Take a look at the file directory structure first

Let’s look at the specific communication methods

1. props & $emit

1.1 the father the sonprops

Now we’ll pass an array list from the Index page to the A page

// index.vue
<template>
  <div>
    <A :list="list" />
  </div>
</template>
<script>

import A from "./components/A";
export default {
  name: "Index".components: {
   A
  },
  data() {
   return {
     list: ["html"."css"."js"]}; }},</script>
Copy the code
// A.vue
<template>
  <div>
    <h2>Page A</h2>
    <ul v-for="(item, index) in list" :key="index">
      <li>{{ item }}</li>
    </ul>
  </div>
</template>
<script>
export default {
  name: "A".props: ["list"]};</script>
Copy the code

In this case, the List array is passed from the Index page to component A in A top-down fashion, and props can only be transmitted from one level to the next, A one-way data flow

1.2 the father$emit

What if component A wants to send data to the Index page? In the case of a child component sending a value to its parent, it is usually transmitted via an event with $emit

// A.vue
<template>
  <div>
    <h2>Page A</h2>
    <ul v-for="(item, index) in list" :key="index">
       <! -- Define events -->
      <li @click="onItemClick(item)">{{ item }}</li>
    </ul>
  </div>
</template>
<script>
export default {
  name: "A".props: ["list"].methods: {
    onItemClick(item) {
      this.$emit("on-item-click".` '${item}' from page A`); ,}}};</script>
Copy the code

The parent listens for an event name on-item-click on the child

// index.vue
<template>
  <div>
    <! - monitor - >
    <A :list="list" @on-item-click="handleItemClick" />
  </div>
</template>

<script>
import A from "./components/A";

export default {
  name: "Index".components: {
    A,
  },
  data() {
    return {
      list: ["html"."css"."js"]}; },methods: {
    handleItemClick(value) {
      console.log(`In page Index get ${value}`); ,}}};</script>
Copy the code

At this point, when we click on the Li element, we can retrieve the value from the A page on the Index page

2. $children & $parent

  • $parentOf the following types:Vue instance
  • $childrenThe type is Array<.Vue instance>, Need to pay attention to$childrenIt’s not sequential, it’s not reactive

Since both apis take vUE instances, they can access variables, methods, etc., on parent or child components; The usage is as follows:

A variable MSG is defined on the A page to fetch data from the parent component via $parent

// A.vue
<template>
  <div>
    <h2>Page A</h2>
    <p>{{ msg }}</p>
    <ul v-for="(item, index) in $parent.list" :key="index">
      <li @click="onItemClick(item)">{{ item }}</li>
    </ul>
    <hr />
  </div>
</template>
<script>
export default {
  name: "A".data() {
    return {};
  },
  // Get the list in Index
  mounted() {
    console.log('A mounted'.this.$parent.list); }};</script>
Copy the code

Now we get the MSG from page A in the parent component with $children and modify it

// Index.vue
<template>
  <div>
    <A :list="list" @on-item-click="handleItemClick" />
    <button @click="handleChange">change A's msg</button>
  </div>
</template>

<script>
import A from "./components/A";

export default {
  name: "Index".components: {
    A,
  },
  data() {
    return {
      list: ["html"."css"."js"]}; },methods: {
    handleItemClick(value) {
      console.log(`In page Index get ${value}`);
    },
    handleChange() {
      console.log((this.$children[0].msg = "A's data is changed")); ,}}};</script>
Copy the code

3. ref

Ref, if used on a normal DOM element, refers to the DOM element itself; If used on a component, the reference refers to an instance of that component

// index.vue
<template>
  <div>
    <A ref="componentA" :list="list" @on-item-click="handleItemClick" />
    <p ref="p">P tags</p>
  </div>
</template>

<script>
import A from "./components/A";

export default {
  name: "Index".components: {
    A,
  },
  mounted() {
    console.log("componentA ref".this.$refs.componentA);
    console.log("p ref".this.$refs.p); }};</script>
Copy the code

Here is the printed result

When ref and V-for are used together, $refs yields an array

4. provide & inject

  • provide:Object | () => Object
  • inject:Array<string> | { [key: string]: string | Symbol | Object }

This pair of options needs to be used together to allow an ancestor component to inject a dependency into all descendants, no matter how deep, for as long as its upstream and downstream relationships are established

Let’s look at the concrete example: Index -> A -> C

Provide the data in Index first

// index.vue
export default {
  name: "Index".components: {
    A,
  },
  provide: {
    name: "name from Index",},data() {
    return {
      list: ["html"."css"."js"]}; },methods: {
    handleItemClick(value) {
      console.log(`In page Index get ${value}`);
    },
    handleChange() {
      console.log((this.$children[0].msg = "A's data is changed")); ,}}};Copy the code

The value is then retrieved by Inject in the C component

// C.vue
export default {
  name: "C".inject: {
    value: "name".data: {
      from: "data1".default: "1",}},mounted() {
    // Index does not provide the data1 variable in provide, so the C component takes the default value
    console.log("C".this.value, this.data); // C name from Index 1}};Copy the code

5. EventBus($emit, $on, $off)

This is done by creating an empty Vue instance as an event bus, using it to fire events, listen for events, and release events, thus achieving communication between any components, and then, as the project grows, this communication method is still not recommended and difficult to maintain

Using EventBus to communicate involves the following steps

5.1 Create an event bus and export it
// event-bus.js

import Vue from "vue";
export const EventBus = new Vue();
Copy the code
5.2 Sending an Event

There are mainly Index, A, B, C pages

// index.vue
<template>
  <div>
    <A />
    <B />
  </div>
</template>
Copy the code
// A.vue
<template>
  <div>
    <h2 @click="handleClick">Page A</h2>
    <C />
  </div>
</template>
<script>
import C from "./C";
/ / introduction
import { EventBus } from ".. /event-bus.js";

export default {
  name: "A".components: {
    C,
  },
  methods: {
    // Trigger the event
    handleClick() {
      EventBus.$emit("transfer-by-event-bus"."eventBus data"); ,}}};</script>
Copy the code
5.3 Listening to events and receiving data
// B.vue
<template>
  <div>
    <h2>Page B</h2>
  </div>
</template>

<script>
import { EventBus } from ".. /event-bus";
export default {
  name: "B".mounted() {
    // Listen on events
    EventBus.$on("transfer-by-event-bus".value= > console.log("B", value)); // B eventBus data}};</script>
Copy the code
// C.vue

<template>
  <h5>C</h5>
</template>

<script>
import { EventBus } from ".. /event-bus";
export default {
  name: "C".mounted() {
    EventBus.$on("transfer-by-event-bus".value= > console.log("C", value)); // C eventBus data}};</script>
Copy the code
5.4 Removing An Event
import { eventBus } from 'event-bus.js'
EventBus.$off('transfer-by-event-bus', {})
Copy the code

6. $attrs & $listeners

6.1 $attrs

Contains attribute 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

We first pass the name, sex, and age values to component B through the Index page

// index.vue
<template>
  <div>
    <B :name="name" :sex="sex" :age="age" @on-click="handleClick" />
  </div>
</template>

<script>
import B from "./components/B";

export default {
  name: "Index".components: {
    B,
  },
  data() {
    return {
      name: "Lily".sex: "female".age: "20"}; }};</script>
Copy the code

Then in component B we print $attrs to see what value we get. You can see that $attrs returns an object, and the key-value pair is the value we passed to component B on the Index page

<template>
  <div>
    <h2>Page B</h2>
  </div>
</template>

<script>
export default {
  name: "B".mounted() {
    console.log(this.$attrs); // {name: "Lily", sex: "female", age: "20"}
  },
Copy the code

If we accept a variable in the props of component B, what happens? The printed value does not contain the name field obtained by props

<template>
  <div>
    <h2>Page B</h2>
  </div>
</template>

<script>
export default {
  name: "B".props: {
    name: String,},mounted() {
    console.log(this.$attrs); // {sex: "female", age: "20"}
  },
Copy the code

Now we need to pass these variables to D, a child of component B, by calling v-bind=”$attrs”

// B.vue

<template>
  <div>
    <h2>Page B</h2>
    <D v-bind="$attrs" />
  </div>
</template>

<script>
import D from "./D";
export default {
  name: "B".components: {
    D,
  },
  props: {
    name: String,},mounted() {
    console.log(this.$attrs); // {sex: "female", age: "20"}
  },
Copy the code

The D component also gets these values

// D.vue

<template>
  <h5>D</h5>
</template>

<script>
export default {
  name: "D".mounted() {
    console.log(this.$attrs); // {sex: "female", age: "20"}}};</script>
Copy the code
6.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

Whenever a child component needs to call a method on the parent component, it can do so via $Listeners, but only if the method name is defined on the parent component

Let’s start by defining two separate events on the Index page

<template>
  <div>
    <B
      @click1="handeClick1"
      @click2="handeClick2"
    />
  </div>
</template>

<script>
import B from "./components/B";

export default {
  name: "Index".components: {
    B,
  },
  data() {
    return {};
  },
  methods: {
    handeClick1() {
      console.log("1");
    },
    handeClick2() {
      console.log("2"); ,}}};</script>
Copy the code

The child component B calls the method as follows, and now we can print 1 and 2 by clicking on the text one and two respectively

// B.vue
<template>
  <div>
    <h2 @click="$listeners.click1">one</h2>
    <h2 @click="$listeners.click2">two</h2>
  </div>
</template>

<script>
export default {
  name: "B".data() {
    return{}; }};</script>
Copy the code

There is no problem if the $listeners are passed down to the subcomponent D of B, we just need to upload the $listeners

// B.vue

<template>
  <div>
    <h2 @click="$listeners.click1">one</h2>
    <h2 @click="$listeners.click2">two</h2>
    <D v-on="$listeners" />
  </div>
</template>
Copy the code

The D component also calls events just like the B component does

// D.vue

<template>
  <h5 @click="$listeners.click1">D</h5>
</template>
Copy the code

7. Vuex

Vuex is a state management model developed specifically for vuue.js applications that allows developers to focus on data updates rather than data delivery

Vuex consists of the following modules

  • state: Used for data storage, yesstoreUnique data source in
  • gettersWith:vueBased onstateThe data is repackaged to get the data that meets the criteria
  • mutations: Handles synchronization events and is the only way to change state in the store
  • actions: can contain asynchronous operations for submissionmutation, cannot directly change the status
  • modules: similar to a namespace, used for projects to define and operate the state of each module separately for easy maintenance

8. To summarize

Now we will sum up the three scenarios mentioned at the beginning again, but we still need to choose the appropriate communication mode according to the current scenario ~

  • Between parent and child components:props & $emit.$chilren & $parent.ref.EventBus.provide & inject.Vuex
  • Between sibling components:Vuex.EventBus
  • Intergenerational relationship between components:EventBus.$attrs & $listeners.provide & inject.Vuex