Six Ways of Communication between vue Components (Full Version)

  Front end, javascript, node.js, Programmer, vue.js

Preface

Component is one of the most powerful functions of vue.js, and the scope of component instances is independent of each other, which means that data between different components cannot be referenced. In general, components can have the following relationships:
image

As shown in the above figure, A and B, B and C, B and D are father-son relationships, C and D are brotherly relationships, and A and C are intergenerational relationships (possibly across multiple generations).

According to different usage scenarios, how to choose effective communication methods? This is the subject we are going to discuss. This paper summarizes several communication methods between vue components, such as props,$emit/$on、vuex、$parent/$children$attrs/$listenersAnd I hope it will be of some help to my small partners.

Please stamp the code of this articleGithub blog, the paper finally feel shallow, everyone began to knock more code!

Method 1,props/$emit

Parent component a is passed to child component b through props, and B to A is implemented through $ emit in component b and v-on in component a.

1. parent component transmits value to child component

Next, we will illustrate how the parent component passes the value to the child component through an example: how to obtain the data in the parent component App.vue in the child component Users.vueusers:["Henry","Bucky","Emily"]

//App.vue parent component
 <template>
 <div id="app">
 < users v-bind: users = "users" > </users >//the former custom name is convenient for subcomponents to call, while the latter passes the data name
 </div>
 </template>
 <script>
 import Users from "./components/Users"
 export default {
 name: 'App',
 data(){
 return{
 users:["Henry","Bucky","Emily"]
 }
 },
 components:{
 "users":Users
 }
 }
//users subcomponent
 <template>
 <div class="hello">
 <ul>
 < Li v-for = "userin users" > {{user}} </Li >//iterate through the passed values and render to the page
 </ul>
 </div>
 </template>
 <script>
 export default {
 name: 'HelloWorld',
 props:{
 Users:{ // This is the custom name of the child tag in the parent component
 type:Array,
 required:true
 }
 }
 }
 </script>

Summary: The parent component passes data down through props to the child component. Note: There are three forms of data in the component: Data, props, computed

2. The child component transmits the value to the parent component (in the form of an event)

Next, we will illustrate how a child component transfers a value to a parent component through an example: when we click “Vue.js Demo”, the child component transfers the value to the parent component, and the text changes from “transferring a value” to “transferring a value from a child to the parent component”, thus realizing the transfer of the value from the child component to the parent component.

子组件向父组件传值之前

//subcomponent
 <template>
 <header>
 < h1 @ click = "changetitle" > {{title}} </h1 >//bind a click event
 </header>
 </template>
 <script>
 export default {
 name: 'app-header',
 data() {
 return {
 title:"Vue.js Demo"
 }
 },
 methods:{
 changeTitle() {
 This.$emit("titleChanged ","child passes value to parent component ");  //Custom Event Pass Value "Child Pass Value to Parent Component"
 }
 }
 }
 </script>
//parent component
 <template>
 <div id="app">
 < app-header v-on: titleChanged = "updatetitle" > </app-header >//consistent with sub-component titlechanged custom event
 // updateTitle($event) accepts the passed text
 <h2>{{title}}</h2>
 </div>
 </template>
 <script>
 import Header from "./components/Header"
 export default {
 name: 'App',
 data(){
 return{
 Title: "A value was passed."
 }
 },
 methods:{
 UpdateTitle(e){ // declares this function
 this.title = e;
 }
 },
 components:{
 "app-header":Header,
 }
 }
 </script>

Summary: When a child component sends a message to a parent component through events, it is actually the child component that sends its own data to the parent component.

Method 2,$emit/$on

This method uses an empty Vue instance as the central event bus (event center) to trigger events and monitor events. It skillfully and lightly realizes communication between any component, including father and son, brother and cross-level.. When our project is relatively large, we can choose a better state management solution vuex.

1. Specific implementation mode:

var Event=new Vue();
 Event.$emit (event name, data);
 Event.$on (event name, data => {});

2. For example

Assuming that there are three sibling components, namely, component a, component b and component c, how can component c obtain the data of component a or component b

<div id="itany">
 <my-a></my-a>
 <my-b></my-b>
 <my-c></my-c>
 </div>
 <template id="a">
 <div>
 < h3>A component: {{name}}</h3 >
 < button @click="send "> send data to component c < /button >
 </div>
 </template>
 <template id="b">
 <div>
 < H3 > component b: {{age}}</h3 >
 < button @click="send "> send array to c component < /button >
 </div>
 </template>
 <template id="c">
 <div>
 < h3>C component: {{name}}, {{age}}</h3 >
 </div>
 </template>
 <script>
 var Event = new Vue();  //define an empty Vue instance
 var A = {
 template: '#a',
 data() {
 return {
 name: 'tom'
 }
 },
 methods: {
 send() {
 Event.$emit('data-a', this.name);
 }
 }
 }
 var B = {
 template: '#b',
 data() {
 return {
 age: 20
 }
 },
 methods: {
 send() {
 Event.$emit('data-b', this.age);
 }
 }
 }
 var C = {
 template: '#c',
 data() {
 return {
 name: '',
 age: ""
 }
 },
 Mounted() {// is executed after the template compilation is completed
 Event.$on('data-a',name => {
 this.name = name;  //no new this will be generated inside the arrow function. if = >isnot used here, this refers to Event
 })
 Event.$on('data-b',age => {
 this.age = age;
 })
 }
 }
 var vm = new Vue({
 el: '#itany',
 components: {
 'my-a': A,
 'my-b': B,
 'my-c': C
 }
 });
 </script>

image
$onCustom events data-a and data-b are monitored, because sometimes it is uncertain when the event will be triggered, and it is usually monitored in the mounted or created hook.

Method 3, vuex

image

1. Briefly introduce Vuex principle

Vuex implements a one-way data flow and has a State to store data in the whole world. when a component wants to change the data in the State, it must do so through Mutation. Mutation also provides subscriber mode for external plug-ins to call to obtain updates of State data. However, when all asynchronous operations (usually calling back-end interface to obtain updated data asynchronously) or batch synchronous operations need to go through Action, but Action cannot directly modify State, or the data of State needs to be modified through Mutation. Finally, according to the change of State, render it to the view.

2. Briefly introduce the functions of each module in the process:

  • Vue Components:Vue components. On the HTML page, it is responsible for receiving interactive behaviors such as user operations and executing the dispatch method to trigger the corresponding action to respond.
  • Dispatch: The trigger method of operation behavior is the only method capable of executing action.
  • actions:The operation behavior processing module consists ofDispatch ('action name', data1)To trigger. Then commit () triggers the invocation of mutation and indirectly updates state. Responsible for handling all interactions received by vueccomponents. Contains synchronous/asynchronous operations, supports multiple methods with the same name, and triggers in sequence according to the registration order. Operations requested from the background API are carried out in this module, including triggering other action and submitting mutation. This module provides the package of Promise to support chain triggering of action.
  • Commit: state change commit operation method. Submitting mutation is the only way to execute mutation.
  • mutations:State change operation method, by actionsCommit('mutation name')To trigger. It is the only recommended method for Vuex to modify state. This method can only be synchronized, and the method name can only be globally unique. During the operation, some hook will be exposed for state monitoring, etc.
  • State: Page State Management Container Object. Centralized storage of scattered data of data objects in Vue components, globally unique, for unified state management. The data required for page display is read from the object, and Vue’s fine-grained data response mechanism is used for efficient state update.
  • Getters:state object reading method. The module is not listed separately in the figure and should be included in the render. Vue Components reads the global state object through this method.

3.Vuex and localStorage

Vuex is vue’s status manager, and the stored data is responsive. But it will not be saved and will return to its original state after refreshing.The specific method should be to save a copy of the data in localStorage when the data in vuex changes. after refreshing, if there is saved data in localStorage, take it out and replace the state in store.

Let defaultCity = "shanghai"
 Try {// The user turned off the local storage function and added a try...catch to the outer layer at this time
 if (!  defaultCity){
 defaultCity = JSON.parse(window.localStorage.getItem('defaultCity'))
 }
 }catch(e){}
 export default new Vuex.Store({
 state: {
 city: defaultCity
 },
 mutations: {
 changeCity(state, city) {
 state.city = city
 try {
 window.localStorage.setItem('defaultCity', JSON.stringify(state.city));
 //when the data changes, copy the data and save it in localStorage.
 } catch (e) {}
 }
 }
 })

It should be noted here that since in vuex, all the states we save are arrays, while localStorage only supports strings, JSON conversion is required:

JSON.stringify(state.subscribeList);  // array -> string
 JSON.parse(window.localStorage.getItem("subscribeList"));  // string -> array

Method 4,$attrs/$listeners

1. Introduction

When multi-level component nesting needs to transfer data, the commonly used method is vuex. However, it would be a bit overkill to use vuex processing if only the data is transferred without intermediate processing. This VUE version 2.4 provides another method–.$attrs/$listeners

  • $attrs: Contains attribute bindings (except class and style) in the parent scope that are not recognized (and obtained) by prop. When a component does not declare any prop, all parent scope bindings (except class and style) are included here and can be passed into internal components through v-bind=”$attrs “. It is usually used in conjunction with interitAttrs option.
  • $listeners: Contains the v-on event listener in the parent scope (without the. native modifier). It can be passed into internal components via v-on=”$listeners “

Next, let’s look at an example of cross-level communication:

// index.vue
 <template>
 <div>
 < h2 > sailing in the waves < /h2 >
 <child-com1
 :foo="foo"
 :boo="boo"
 :coo="coo"
 :doo="doo"
 Title= "front-end craftsman"
 ></child-com1>
 </div>
 </template>
 <script>
 const childCom1 = () => import("./childCom1.vue");
 export default {
 components: { childCom1 },
 data() {
 return {
 foo: "Javascript",
 boo: "Html",
 coo: "CSS",
 doo: "Vue"
 };
 }
 };
 </script>
// childCom1.vue
 <template class="border">
 <div>
 <p>foo: {{ foo }}</p>
 < p>childCom1 $attrs: {{ $attrs }}</p >
 <child-com2 v-bind="$attrs"></child-com2>
 </div>
 </template>
 <script>
 const childCom2 = () => import("./childCom2.vue");
 export default {
 components: {
 childCom2
 },
 InheritAttrs: false, // can turn off attributes that are automatically mounted on the component root element and are not declared in props
 props: {
 Foo: String // foo bound as props property
 },
 created() {
 console.log(this.$attrs);  //{"boo": "html", "coo": "CSS", "doo": "vue", "title": "front-end craftsman"}
 }
 };
 </script>
// childCom2.vue
 <template>
 <div class="border">
 <p>boo: {{ boo }}</p>
 <p>childCom2: {{ $attrs }}</p>
 <child-com3 v-bind="$attrs"></child-com3>
 </div>
 </template>
 <script>
 const childCom3 = () => import("./childCom3.vue");
 export default {
 components: {
 childCom3
 },
 inheritAttrs: false,
 props: {
 boo: String
 },
 created() {
 console.log(this.$attrs);  //{"COO": "CSS", "DOO": "Vue", "Title": "Front End Craftsman"}
 }
 };
 </script>
// childCom3.vue
 <template>
 <div class="border">
 <p>childCom3: {{ $attrs }}</p>
 </div>
 </template>
 <script>
 export default {
 props: {
 coo: String,
 title: String
 }
 };
 </script>

image
As shown in the figure above$attrsRepresents an object that does not inherit data and has the format {Attribute Name: Attribute Value}. Vue2.4 provides$attrs,$listenersTo transfer data and events, communication between cross-level components becomes easier.

In short:$attrsAnd$listenersIt’s two objects,$attrsIs stored in the parent component binding non-Props properties,$listenersThe store is a non-native event bound in the parent component.

Method v. provision/injection

1. Introduction

Vue2.2.0 adds API, which needs to be used together.To allow an ancestor component to inject a dependency into all its descendants, no matter how deep the component level is, and it will always take effect when the upstream-downstream relationship is established.. In a nutshell: variables are provided by provider in the ancestor component and then injected by inject in the descendant component.
The provide/inject API mainly solves the communication problem between cross-level components. However, its usage scenario is that sub-components obtain the state of higher-level components. A relationship between active provision and dependency inject ion is established between cross-level components..

2. For example

Suppose there are two components: A.vue and b.vue. b is a subcomponent of a.

// A.vue
 export default {
 provide: {
 Name:' Sailing in the Waves'
 }
 }
// B.vue
 export default {
 inject: ['name'],
 mounted () {
 console.log(this.name);  //Sailing in the Waves
 }
 }

As you can see, in A.vue, we set up aprovide: name, value for the wave in the boat, its role is tonameThis variable is provided to all its subcomponents. And in B.vue, throughinjectInjected with thenameVariable, then in component b, it can be passed directly throughthis.nameAfter accessing this variable, its value is the same as sailing in the waves. This is the core usage of the provider/injectapi.

It should be noted that:Provide and inject bindings are not responsive. This is deliberately done. However, if you pass in a monitorable object, its object’s properties are still responsive—-vue Official Documents
Therefore, if the name of A.vue above changes, the name of B.vue’s  this.name  will not change, and it will still be sailing on the waves.

3. How do 3.provide and inject implement data response

In general, there are two ways:

  • Provide an instance of an ancestor component, and then inject dependencies into the descendant component, so that the attributes of the instance of the ancestor component can be directly modified in the descendant component. However, this method has the disadvantage that many unnecessary things such as props and methods are mounted on this instance.
  • Use the latest API Vue.observable in 2.6 to optimize the responsive provide (recommended)

Let’s look at an example: sun components d, e and f obtain the color value passed by component a and can realize data responsive change, i.e. components d, e and f will not change after the color of component a changes (core code is as follows:)

image

// A component
 <div>
 < h1>A component < /h1 >
 Color</button @click="() => changeColor () "> changecolor </button >
 <ChildrenB />
 <ChildrenC />
 </div>
 ......
 data() {
 return {
 color: "blue"
 };
 },
 // provide() {
 //   return {
 //     theme: {
 // color: this.color // data bound in this way is not responsive
 //}//i.e. after the color of component a changes, components d, e and f will not change accordingly.
 //   };
 // },
 provide() {
 return {
 Theme: this///method one: provide an instance of the ancestor component
 };
 },
 methods: {
 changeColor(color) {
 if (color) {
 this.color = color;
 } else {
 this.color = this.color === "blue" ?   "red" : "blue";
 }
 }
 }
 //Method 2: Use the latest API Vue.observable in 2.6 to optimize the responsive provide
 // provide() {
 //   this.theme = Vue.observable({
 //     color: "blue"
 //   });
 //   return {
 //     theme: this.theme
 //   };
 // },
 // methods: {
 //   changeColor(color) {
 //     if (color) {
 //       this.theme.color = color;
 //     } else {
 //       this.theme.color = this.theme.color === "blue" ?   "red" : "blue";
 //     }
 //   }
 // }
// F component
 <template functional>
 <div class="border2">
 < H3: style = "{color: injections.theme.color}" > f component < /h3 >
 </div>
 </template>
 <script>
 export default {
 inject: {
 theme: {
 //Functional components have different values
 default: () => ({})
 }
 }
 };
 </script>

Although provide and inject mainly provide use cases for high-level plug-ins/component libraries, if you can use them skillfully in your business, you can get twice the result with half the effort!

Method 6,$parent/$childrenAndref

  • ref: If used on ordinary DOM elements, the reference refers to DOM elements; If used on a subcomponent, the reference points to the component instance
  • $parent/$children: Access parent/child instances

It should be noted that both of them directly obtain component instances and can directly call component methods or access data after use. Let’s take a look firstrefTo access an example of a component:

// component-a subcomponent
 export default {
 data () {
 return {
 title: 'Vue.js'
 }
 },
 methods: {
 sayHello () {
 window.alert('Hello');
 }
 }
 }
//parent component
 <template>
 <component-a ref="comA"></component-a>
 </template>
 <script>
 export default {
 mounted () {
 const comA = this.$refs.comA;
 console.log(comA.title);  // Vue.js
 comA.sayHello();  //popup windows
 }
 }
 </script>

However,The disadvantage of these two methods is that they cannot communicate between different levels or brothers..

// parent.vue
 <component-a></component-a>
 <component-b></component-b>
 <component-b></component-b>

We want to access the two component-b components in the page that references it (here is parent.vue) in Component-A. In this case, we have to configure additional plug-ins or tools, such as Vuex and Bus solutions.

Summary

Common usage scenarios can be divided into three categories:

  • Parent-child communication:

The parent passes data to the child through props, and the child passes data to the parent through events ($emit); Communication is also possible through parent/child chains ($parent/$children); Ref can also access component instances; provide / inject API;$attrs/$listeners

  • Brother correspondence:

Bus; Vuex

  • Cross-level communication:

Bus; Vuex; provide / inject API、$attrs/$listeners

To recommend a useful BUG monitoring toolFundebug, welcome to try free!

Welcome to pay attention to the public number:Front end craftsmanWe witness your growth together!
image

Reference article