The communication mode of VUE components was a very frequent question in the interview. When I started looking for internship, I often encountered this question. At that time, I only knew how to go back to props and $emit. Today, vUE component communication mode is summarized, if there are omissions in writing, please kindly comment.

1. props/$emit

Introduction to the

Props and $emit are familiar with this, which is our most common vUE communication method. Props: props can be arrays or objects that receive data passed from the parent component via V-bind. When props is an array, the properties passed by the parent component are received. If props is an object, you can configure the type, default, Required, and validator of the properties, and set the default value, required, and verification rules. $emit: When parent and child components communicate, we usually use $emit to trigger the parent component V-ON to bind the corresponding event on the child component.

The code examples

To implement the parent component communication between props and $emit, in this example we implement the following communication:

  • Parent to child: The parent component passes:messageFromParent="message"The parent component message value is passed to the child component, and when the parent component’s input tag is entered, the content in the child component’s P tag changes accordingly.

  • Child to parent: The parent component passes@on-receive="receive"The receive callback is triggered when the child component’s input tag is enteredthis.$emit('on-receive', this.message)Assign the child message value to the parent messageFromChild, changing the contents of the parent p tag.

Look at the code:

// Subcomponent code<template>
  <div class="child">
    <h4>this is child component</h4>
    <input type="text" v-model="message" @keyup="send" />
    <p>Message received from parent component: {{messageFromParent}}</p>
  </div>
</template>
<script>
export default {
  name: 'Child'.props: ['messageFromParent'].// Receive a message from the parent component via props
  data() {
    return {
      message: ' ',}},methods: {
    send() {
      this.$emit('on-receive'.this.message)  // Emit the on-receive event via $emit, calling the receive callback from the parent component with this.message as an argument}},}</script>
Copy the code
// Parent component code<template>
  <div class="parent">
    <h3>this is parent component</h3>
    <input type="text" v-model="message" />
    <p>Message received from child component: {{messageFromChild}}</p>
    <Child :messageFromParent="message" @on-receive="receive" />
  </div>
</template>
<script>
import Child from './child'
export default {
  name: 'Parent'.data() {
    return {
      message: ' '.// Messages passed to child components
      messageFromChild: ' ',}},components: {
    Child,
  },
  methods: {
    receive(msg) { // Take the child's information and assign it to messageFromChild
      this.messageFromChild = msg
    },
  },
}
</script>
Copy the code

Results the preview

2. v-slot

Introduction to the

V-slot is an API added in Vue2.6 for unified implementation of slots and named slots. It replaces the apis of slot(deprecated in 2.6.0), slot-scope(deprecated in 2.6.0), and Scope (deprecated in 2.5.0). V-slot in the template tag is used to provide a named slot or a slot that needs to receive prop. If v-slot is not specified, the default value is default.

The code examples

Here is a code example of v-slot, in which we implement:

  • Parent to child: The parent component passes<template v-slot:child>{{ message }}</template>The message value of the parent component is passed to the child component, which passes<slot name="child"></slot>Received the corresponding content, the parent to the child to achieve the value.
// Subcomponent code<template>
  <div class="child">
    <h4>this is child component</h4>
    <p>Message received from parent component:<slot name="child"></slot>  <! {{message}}-->
    </p>
  </div>
</template>
Copy the code
<template>
  <div class="parent">
    <h3>this is parent component</h3>
    <input type="text" v-model="message" />
    <Child>
      <template v-slot:child>
        {{ message }}  <! -- Slot to display content -->
      </template>
    </Child>
  </div>
</template>
<script>
import Child from './child'
export default {
  name: 'Parent'.data() {
    return {
      message: ' ',}},components: {
    Child,
  },
}
</script>
Copy the code

Results the preview

3. $refs/$parent/$children/$root

Introduction to the

We can also use $refs/$parent/$children/$root to get the Vue component instance and get the properties and methods bound on the instance to achieve communication between components. $refs: We usually bind $refs to a DOM element to get the attributes of the DOM element. To implement component communication, we can also bind $refs to the child component to obtain the child component instance. $parent: We can get the parent instance of the current component (if any) directly in Vue via this.$parent. $children: Similarly, we can get an array of child instances of the current component in Vue directly through this.$children. Note, however, that the subscripts of the elements in this.$children array do not necessarily correspond to the order of the children referenced by the parent. For example, children loaded asynchronously may affect their order in the children array. Therefore, it is necessary to find the corresponding sub-component according to certain conditions, such as the name of the sub-component. $root: Gets the root Vue instance of the current component tree. If the current instance has no parent, the instance will be itself. With $root, we can enable cross-level communication between components.

The code examples

$refs $parent $children $refs $parent $children $refs $parent $children $refs

  • Parent passes value to child: child passes$parent.messageGets the value of message from the parent component.
  • Child to parent: The parent component passes$childrenGet an array of child component instances, then iterate through the array, get the corresponding child component instance of Child1 by instance name, assign it to Child1, and passchild1.messageMessage for the child component Child1.

The code is as follows:

/ / child component<template>
  <div class="child">
    <h4>this is child component</h4>
    <input type="text" v-model="message" />
    <p>{{$parent. Message}}</p>  <! -- Display message of parent component instance -->
  </div>
</template>
<script>
export default {
  name: 'Child1'.data() {
    return {
      message: ' '.// The parent component can get the message of the child instance through this.$children}}},</script>
Copy the code
/ / the parent component<template>
  <div class="parent">
    <h3>this is parent component</h3>
    <input type="text" v-model="message" />
    <p>Received message from child component: {{child1.message}}</p> <! -- Display message of child component instance -->
    <Child />
  </div>
</template>
<script>
import Child from './child'
export default {
  name: 'Parent'.data() {
    return {
      message: ' '.child1:}}, {},components: {
    Child,
  },
  mounted() {
    this.child1 = this.$children.find((child) = > {
      return child.$options.name === 'Child1'  // Use options.name to obtain the child instance corresponding to name}})},</script>
Copy the code

Results the preview

4. $attrs/$listener

Introduction to the

$Listeners and $attrs are new to Vue2.4 and are intended to be used by users to develop advanced components. $attrs: Used to receive attributes that are not recognized as prop in the parent scope, and can be passed to internal components via V-bind =”$attrs” — useful when creating high-level components. Imagine when you create a component and you receive param1, param2, param3… If you want to pass props, you need to pass props: [‘param1’, ‘param2’, ‘param3’…] A lot of statements. If some of these props also need to be passed to deeper sub-components, that would be even more troublesome. $attrs. Param1, $attrs.param2… Can be used, and it is very convenient to pass the sample above to the deeper child components. $Listeners: Contains V-ON event listeners in the parent scope. Internal components can be passed in via V-on =”$Listeners “– useful for creating higher-level components, and used in much the same way as $attrs.

The code examples

In this example, there are three components: A, B, and C. The relationship is as follows: [A [B [C]], A is the parent component of B, and B is the parent component of C. That is, level 1 component A, Level 2 component B, and level 3 component C. We achieved:

  • Parent to child: level 1 component A passes:messageFromA="message"The message property is passed to level 2 component B, which passes$attrs.messageFromAThe message of level 1 component A is obtained.
  • Cascading value: Level 1 component A passes:messageFromA="message"The Message property is passed to level 2 component B, which passes through v-bind="$attrs"It is passed to level 3 component C, which passes$attrs.messageFromAThe message of level 1 component A is obtained.
  • Child to parent: Level 1 component A passes@keyup="receive"Bind keyUP event listener on descendant component, level 2 component B is passingv-on="$listeners"To bind the KeyUp event to its input tag. When the input box of level 2 component B is entered, the receive callback of level 1 component A is triggered, and the value in the input box of level 2 component B is assigned to the messageFromComp of level 1 component A, thus realizing the transmission of value from child to parent.
  • Cascading value: Level 1 component A passes@keyup="receive"Bind keyUP event listener on descendant component, level 2 component B is passing <CompC v-on="$listeners" />And pass it on to C. Level 3 component C is passingv-on="$listeners"To bind the KeyUp event to its input tag. When the input box of level 3 component C input is entered, the receive callback of level 1 component A is triggered, and the value in the input box of level 3 component C is assigned to messageFromComp of Level 1 component A, thus achieving cross-level upward transfer of values.

The code is as follows:

// Level 3 component C<template>
  <div class="compc">
    <h5>this is C component</h5>
    <input name="compC" type="text" v-model="message" v-on="$listeners" /> <! Bind A listener callback to the input for keyUp -->
    <p>{{$attrs.messageFromA}}</p>
  </div>
</template>
<script>
export default {
  name: 'Compc'.data() {
    return {
      message: ' ',}}}</script>
Copy the code
// Level 2 component B<template>
  <div class="compb">
    <h4>this is B component</h4>
    <input name="compB" type="text" v-model="message" v-on="$listeners" />  <! Bind A listener callback to the input for keyUp -->
    <p>{{$attrs.messageFromA}}</p>
    <CompC v-bind="$attrs" v-on="$listeners" /> <! -- Pass A keyup callback to C and attrs to C -->
  </div>
</template>
<script>
import CompC from './compC'
export default {
  name: 'CompB'.components: {
    CompC,
  },
  data() {
    return {
      message: ' ',}}}</script>
Copy the code
/ / A component<template>
  <div class="compa">
    <h3>this is A component</h3>
    <input type="text" v-model="message" />
    <p>{{comp}} {{messageFromComp}}</p>
    <CompB :messageFromA="message" @keyup="receive" />  <! -- Listen for descendant component keyUP event, pass message to descendant component -->
  </div>
</template>
<script>
import CompB from './compB'
export default {
  name: 'CompA'.data() {
    return {
      message: ' '.messageFromComp: ' '.comp: ' ',}},components: {
    CompB,
  },
  methods: {
    receive(e) { // Listen for the callback of the descendant component's keyUP event and assign the value of the keyUp input field to messageFromComp
      this.comp = e.target.name
      this.messageFromComp = e.target.value
    },
  },
}
</script>
Copy the code

Results the preview

5. provide/inject

Introduction to the

Provide /inject This pair of options needs to be used together to allow an ancestor component to inject a dependency to all of its descendants, no matter how deep the component hierarchy is, and remain in effect as long as its upstream and downstream relationships are established. If you’re familiar with React, you’ll immediately think of the Context API. It’s very similar. Provide: is an object, or a function that returns an object. This object contains properties that can be injected into its descendants, that is, properties and property values to be passed to descendants. Injcet: An array of strings, or an object. When it is an array of strings, it is used in much the same way as props, except that the properties received are properties in provide instead of data. When it is an object, it is similar to props. You can configure properties like default and FROM to set the default value, use new naming properties in child components, and so on.

The code examples

This example has three components, level 1 component A, level 2 component B, and level 3 component C: [A [B [C]], where A is the parent of B and B is the parent of C. The example implements:

  • Parent to child: Level 1 component A injects message to descendant components through provide, level 2 component B throughinject: ['messageFromA']To receive the message from level 1 component A and passmessageFromA.contentGets the value of the Content property of Message in level 1 component A.
  • Cascading values across levels: Level 1 component A injects message to descendant components through provide, and level 3 component C throughinject: ['messageFromA']To receive the message from level 1 component A and passmessageFromA.contentGets the value of the Content property of Message in level 1 component A for cascading values.

The code is as follows:

// Level 1 component A<template>
  <div class="compa">
    <h3>this is A component</h3>
    <input type="text" v-model="message.content" />
    <CompB />
  </div>
</template>
<script>
import CompB from './compB'
export default {
  name: 'CompA'.provide() {
    return {
      messageFromA: this.message,  // Pass message to descendant components via provide}},data() {
    return {
      message: {
        content: ' ',}}},components: {
    CompB,
  },
}
</script>
Copy the code
// Level 2 component B<template>
  <div class="compb">
    <h4>this is B component</h4>
    <p>{{messageFromA && messageFroma.content}}</p>
    <CompC />
  </div>
</template>
<script>
import CompC from './compC'
export default {
  name: 'CompB'.inject: ['messageFromA'].// Inject the message passed by provide in A
  components: {
    CompC,
  },
}
</script>
Copy the code
// Level 3 component C<template>
  <div class="compc">
    <h5>this is C component</h5>
    <p>{{messageFromA && messageFroma.content}}</p>
  </div>
</template>
<script>
export default {
  name: 'Compc'.inject: ['messageFromA'].// Inject the message passed by provide in A
}
</script>
Copy the code

Note:

  1. Some students may want to ask me why the message in level 1 component A is of object type instead of string type, because vue provide and Inject binding are not responsive. If message is A string, it cannot be assigned to messageFromA after changing the value of Message through the input box in level 1 component A. If it is object, the property value in messageFromA can be changed after the object property value is changed. The object property value received by the descendant component inject can also change accordingly.
  2. Progeny provide overrides the ancestor’s provide value for the same attribute as the ancestor. For example, if level 2 component B also infuses A messageFromA value into Level 3 component C by providing it, Then messageFromA in Level 3 component C will preferentially receive the value injected by Level 2 component B rather than Level 1 component A.

Results the preview

6. eventBus

Introduction to the

EventBus, also known as eventBus, registers a new Vue instance, calls $emit and $on of the instance to monitor and trigger the event of the instance, and passes in parameters to achieve global communication of components. It’s a component that doesn’t have the DOM, just its instance methods, so it’s very lightweight. We can do this by registering on global Vue instances:

// main.js
Vue.prototype.$Bus = new Vue()
Copy the code

But when the project is too large, it is best to abstract the event bus into a single file and import it into each component file you need to use. This way, it does not pollute the global namespace:

// bus.js, which is imported with import when used
import Vue from 'vue'
export const Bus = new Vue()
Copy the code

The principle of analysis

$emit and $ON = $emit and $ON = $emit and $ON = $emit and $ON = $emit

/ / eventBus principle
export default class Bus {
  constructor() {
    this.callbacks = {}
  }
  $on(event, fn) {
    this.callbacks[event] = this.callbacks[event] || []
    this.callbacks[event].push(fn)
  }
  $emit(event, args) {
    this.callbacks[event].forEach((fn) = > {
      fn(args)
    })
  }
}

// Introduce the following in main.js
// Vue.prototype.$bus = new Bus()
Copy the code

The code examples

In this example, there are four components :[A [B [C, D]], level 1 component A, level 2 component B, level 3 component C, and level 3 component D. We implemented this by using eventBus:

  • Global communication: that is, parent and child components communicate with each other, sibling components communicate with each other, and cross-level components communicate with each other. The operation logic of the four components is the same, they are all passed in the input boxthis.$bus.$emit('sendMessage', obj)Trigger the sendMessage event callback, which wraps sender and Message as objects passed in as arguments; At the same time throughthis.$bus.$on('sendMessage', obj)Listen for sendMessage events from other components, instantiating the values of the current component sample sender and Message. In this way, when the value of the input box of any component changes, other components can receive corresponding information to achieve global communication.

The code is as follows:

// main.js
Vue.prototype.$bus = new Vue()
Copy the code
// Level 1 component A<template>
  <div class="containerA">
    <h2>this is CompA</h2>
    <input type="text" v-model="message" @keyup="sendMessage" />
    <p v-show="messageFromBus && sender ! == $options.name">{{sender}} {{messageFromBus}}</p>
    <CompB />
  </div>
</template>
<script>
import CompB from './compB'
export default {
  name: 'CompA'.components: {
    CompB,
  },
  data() {
    return {
      message: ' '.messageFromBus: ' '.sender: ' ',}},mounted() {
    this.$bus.$on('sendMessage'.(obj) = > {  // Listen for sendMessage events via eventBus
      const { sender, message } = obj
      this.sender = sender
      this.messageFromBus = message
    })
  },
  methods: {
    sendMessage() {
      this.$bus.$emit('sendMessage', { // Trigger the sendMessage event via eventBus
        sender: this.$options.name,
        message: this.message,
      })
    },
  },
}
</script>
Copy the code
// Level 2 component B<template>
  <div class="containerB">
    <h3>this is CompB</h3>
    <input type="text" v-model="message" @keyup="sendMessage" />
    <p v-show="messageFromBus && sender ! == $options.name">{{sender}} {{messageFromBus}}</p>
    <CompC />
    <CompD />
  </div>
</template>
<script>
import CompC from './compC'
import CompD from './compD'
export default {
  name: 'CompB'.components: {
    CompC,
    CompD,
  },
  data() {
    return {
      message: ' '.messageFromBus: ' '.sender: ' ',}},mounted() {
    this.$bus.$on('sendMessage'.(obj) = > { // Listen for sendMessage events via eventBus
      const { sender, message } = obj
      this.sender = sender
      this.messageFromBus = message
    })
  },
  methods: {
    sendMessage() {
      this.$bus.$emit('sendMessage', { // Trigger the sendMessage event via eventBus
        sender: this.$options.name,
        message: this.message,
      })
    },
  },
}
</script>
Copy the code
// Level 3 component C<template>
  <div class="containerC">
    <p>this is CompC</p>
    <input type="text" v-model="message" @keyup="sendMessage" />
    <p v-show="messageFromBus && sender ! == $options.name">{{sender}} {{messageFromBus}}</p>
  </div>
</template>
<script>
export default {
  name: 'CompC'.data() {
    return {
      message: ' '.messageFromBus: ' '.sender: ' ',}},mounted() {
    this.$bus.$on('sendMessage'.(obj) = > { // Listen for sendMessage events via eventBus
      const { sender, message } = obj
      this.sender = sender
      this.messageFromBus = message
    })
  },
  methods: {
    sendMessage() {
      this.$bus.$emit('sendMessage', { // Trigger the sendMessage event via eventBus
        sender: this.$options.name,
        message: this.message,
      })
    },
  },
}
</script>
Copy the code
// Level 3 component D<template>
  <div class="containerD">
    <p>this is CompD</p>
    <input type="text" v-model="message" @keyup="sendMessage" />
    <p v-show="messageFromBus && sender ! == $options.name">{{sender}} {{messageFromBus}}</p>
  </div>
</template>
<script>
export default {
  name: 'CompD'.data() {
    return {
      message: ' '.messageFromBus: ' '.sender: ' ',}},mounted() {
    this.$bus.$on('sendMessage'.(obj) = > { // Listen for sendMessage events via eventBus
      const { sender, message } = obj
      this.sender = sender
      this.messageFromBus = message
    })
  },
  methods: {
    sendMessage() {
      this.$bus.$emit('sendMessage', { // Trigger the sendMessage event via eventBus
        sender: this.$options.name,
        message: this.message,
      })
    },
  },
}
</script>
Copy the code

Results the preview

7. Vuex

When the project is large and many people maintain the same project, it is easy to make the change of global variables difficult to predict if the event bus is used for global communication. And so Vuex was born. Vuex is a state management mode developed specifically for vue.js applications. It uses centralized storage to manage the state of all components of an application and rules to ensure that the state changes in a predictable way. For more information about Vuex, please refer to the official Vuex documentation. I won’t be teaching you how to use the Vuex code.

The code examples

The Vuex instance and event bus, Leisi, also contains four components :[A [B [C, D]], Level 1 component A, Level 2 component B, level 3 component C, and level 3 component D. In this example we implement:

  • Global communication: The content of the code is similar to eventBus, but much easier to use. Each component monitors the change of the input box through Watch, and triggers mutations through VUex’s COMMIT to change the value of stroe. Each component then obtains the data in the Store dynamically through computed, enabling global communication.
// store.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
  state: {
    message: {
      sender: ' '.content: ' ',}},mutations: {
    sendMessage(state, obj) {
      state.message = {
        sender: obj.sender,
        content: obj.content,
      }
    },
  },
})
Copy the code
/ / component A<template>
  <div class="containerA">
    <h2>this is CompA</h2>
    <input type="text" v-model="message" />
    <p v-show="messageFromStore && sender ! == $options.name">{{sender}} {{messageFromStore}}</p>
    <CompB />
  </div>
</template>
<script>
import CompB from './compB'
export default {
  name: 'CompA'.components: {
    CompB,
  },
  data() {
    return {
      message: ' ',}},computed: {
    messageFromStore() {
      return this.$store.state.message.content
    },
    sender() {
      return this.$store.state.message.sender
    },
  },
  watch: {
    message(newValue) {
      this.$store.commit('sendMessage', {
        sender: this.$options.name,
        content: newValue,
      })
    },
  },
}
</script>
Copy the code

As in eventBus, the code in B, C, and D components is the same except for the differences in the subcomponents.

Results the preview

conclusion

In total, the component communication modes of Vue in 7 are mentioned above. The types of communication they can carry out are as follows:

  • Props /$EMIT: Supports bidirectional communication between parent and child components, which is our most common choice for daily parent-child communication.
  • V-slot: Enables one-way communication between parent and child components (the parent transmits values to the child). V-slot is preferred when implementing reusable components, passing DOM nodes, HTML and other content into components, and secondary processing of table values in some component libraries.
  • $refs/$parent/$children/$root: can implement parent-child component bidirectional communication, where $root can implement the root component instance to the descendant component one-way value across the level. Consider using these apis when parents want to get each other’s properties or methods when the parent component is not passing values or listening through v-ON bindings.
  • $Listeners can easily retrieve incoming attributes and bindings and pass them on to sub-components, making them useful for building advanced components.
  • Provide /inject: Enables cross-level unidirectional communication and lightweight injection of dependencies into descendant components, which is the only choice when implementing advanced components and creating component libraries.
  • EventBus: Enables global communication. If the project size is small, eventBus can be used to monitor events globally. However, use eventBus with caution to avoid global contamination and memory leaks.
  • Vuex: Enables global communication and is a best practice for global state management of VUE projects. When a project is large and you want to centrally manage the state of global components, you can install Vuex.

Finally, Lu Xun said: “a bowl of hot and sour soup, what you hear by mouth, is not as clear as sipping it yourself. (Lu Xun: THIS sentence I really said!) Read so much, as their own hands to knock a knock more understanding, read can go to manually knock a deeper understanding.