Vue component communication values (parent-child component communication / brother component communication / cross level communication)

First: Props / $emit

Father child Props

// Parent component
<template> <div id="app"> <users v-bind:users="users"></users>//The former is easy to call for subcomponents, while the latter needs to pass data names </div> </template> <script> import Users from "./components/Users" export default { name: 'App', data(){ return{ users:["Henry","Bucky","Emily"] } }, components:{ "users":Users } }

  

// Subcomponent
<template> <div class="hello"> <ul> <li v-for="user in users">{{user}}</li>//Traverses the passed value and renders it to the page </ul> </div> </template> <script> export default { name: 'HelloWorld', props:{ users:{ //This is the custom name of the child tag of the parent component type:Array, required:true } } } </script>

  

Child to parent $emit

It is usually applied to the occurrence of certain events in a child component, and the parent component needs to be told what to do. For example: https://m.jd.com/ Classification: the navigation list on the left is a component, and the content part on the right is a component. When a user clicks an item in the navigation list, he / she needs to tell the parent component who clicked, and then the parent component requests data to display the corresponding content.

// 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","Pass value from child to parent");//Custom event pass value child pass value to parent
    }
  }
}
</script>

  

// Parent component
<template>
  <div id="app">
    <app-header v-on:titleChanged="updateTitle" ></app-header>//Consistent with the 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 is passed"
    }
  },
  methods:{
    updateTitle(e){   //Declare this function
      this.title = e;
    }
  },
  components:{
   "app-header":Header,
  }
}
</script>

  

Second: $emit / $on

This method uses an empty Vue instance as the central event bus (event center) to trigger events and listen to events. It cleverly and lightly realizes the communication between any component, including parent-child, brother, cross level. When our project is large, we can choose a better state management solution, vuex.
Specific implementation mode
var Event=new Vue();
    Event.$emit(Event name,data);
    Event.$on(Event name,data => {});

  

Example:

Suppose that there are three sibling components: A, B, and C. how can C obtain the data of a or B

<div id="itany">
    <my-a></my-a>
    <my-b></my-b>
    <my-c></my-c>
</div>
<template id="a">
  <div>
    <h3>A Components:{{name}}</h3>
    <button @click="send">Send data to C assembly</button>
  </div>
</template>
<template id="b">
  <div>
    <h3>B Components:{{age}}</h3>
    <button @click="send">Send array to C assembly</button>
  </div>
</template>
<template id="c">
  <div>
    <h3>C Components:{{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() {//Execute after template compilation
     Event.$on('data-a',name => {
         this.name = name;//No new this will be generated inside the arrow function. If you do not use = > 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>

  

 
Note: $on listens to the custom events data-a and data-b, because sometimes it's uncertain when the event will be triggered, and it's usually listened in the mounted or created hooks.

Third, use $children / ref

$children strongly do not recommend this method

If a new sub component is added, the index value of $children[0] will also be modified, which is not flexible enough.

// Parent gets child value
// Parent component
this.msg2=this.$children[0].msg

  

ref

If it is used on a normal DOM element, the reference points to the DOM element; if it is used on a subcomponent, the reference points to the component instance.

// 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
    }
  }
</script>

  

// Subcomponent
export default {
  data () {
    return {
      title: 'Vue.js'
    }
  },
  methods: {
    sayHello () {
      window.alert('Hello');
    }
  }
}

  

The drawback of both $children and ref methods is that they cannot communicate across levels or siblings. As follows:
<component-a></component-a>
<component-b></component-b>
<component-b></component-b>

  

Fourth: $attrs / $listeners

When multi-level component nesting needs to transfer data, the common method is through vuex. But if we just transfer the data without intermediate processing and use vuex processing, it's a little overqualified. For this Vue 2.4 release, an alternative method, $attrs / $listeners is provided
  1. $attrs contains attribute bindings (except class and style) that are not recognized (and acquired) by prop in the parent scope. When a component does not declare any prop, all bindings of the parent scope (except class and style) will be included here, and internal components can be passed in through v-bind="$attrs". Usually used with the interitAttrs option.
  2. $listeners contains the v-on event listeners in the parent scope (without the. native modifier). It can pass in internal components through v-on="$listeners".
Take an example of cross level communication:
// index.vue
<template>
  <div>
    <h2>Boating 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 Of $attrs: {{ $attrs }}</p>
    <child-com2 v-bind="$attrs"></child-com2>
  </div>
</template>
<script>
const childCom2 = () => import("./childCom2.vue");
export default {
  components: {
    childCom2
  },
  inheritAttrs: false, // You can turn off properties that are not declared in props that are automatically attached to the component root element
  props: {
    foo: String // foo bound as props attribute
  },
  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>

  

 

Simply put: $attrs and $listeners are two objects. In $attrs, there are bound non Props attributes in the parent component. In $listeners, there are bound non-native events in the parent component.

Fifth: provide / inject

Vue 2.2.0 adds an API, which needs to be used together with options to allow an ancestor component to inject a dependency into all its descendants, no matter how deep the component level is, and always takes effect from the time when the upstream downstream relationship is established. In a word, the 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 problem of communication between cross level components, but its usage scenario is that the sub components get the state of the parent components, and the cross level components establish a relationship between active provision and dependency injection.
For example:

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

// A.vue
export default {
  provide: {
    name: 'Hello'
  }
}

  

// B.vue
export default {
  inject: ['name'],
  mounted () {
    console.log(this.name);  // Hello
  }
}

  

As you can see, in A.vue, we set a provision: name with a value of 'hello'. Its function is to provide the variable name to all its subcomponents. In B.vue, the name variable provided from component A is injected through inject. In component B, you can directly use the this.name Access to this variable, its value is also 'hello'. This is the core usage of the provide / inject API.

Note: the provide and inject bindings are not responsive. This is intentional. However, if you pass in a listener object, its properties are still responsive -- vue official document

So, if the name of A.vue changes, B.vue's this.name It won't change. It's still 'hello'.

How to realize data responsive with provide and inject

There are generally two methods:

Let's take an example: Sun components D, E and f get the color value passed by A component, and can realize data responsive change, that is, after the color of A component changes, components D, E and F will change with it (the core code is as follows:)

// A component 
<div>
      <h1>A assembly</h1>
      <button @click="() => changeColor()">change color</button>
      <ChildrenB />
      <ChildrenC />
</div>
......
  data() {
    return {
      color: "blue"
    };
  },
  // provide() {
  //   return {
  //     theme: {
  //       color: this.color  //The data bound in this way is not responsive
  //     }/ / that is, after the color of component A changes, component D, E and F will not change
  //   };
  // },
  provide() {
    return {
      theme: this//Method 1: provide instances of ancestor components
    };
  },
  methods: {
    changeColor(color) {
      if (color) {
        this.color = color;
      } else {
        this.color = this.color === "blue" ? "red" : "blue";
      }
    }
  }
  // Method 2: use 2.6 latest API Vue.observable  Optimize 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 components 
<template functional>
  <div class="border2">
    <h3 :style="{ color: injections.theme.color }">F assembly</h3>
  </div>
</template>
<script>
export default {
  inject: {
    theme: {
      //Different values of functional components
      default: () => ({})
    }
  }
};
</script>

  

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

Sixth: Vuex status management

1. Briefly introduce the principle of Vuex

Vuex implements a one-way data flow. It has a State to store data in the global. When the component wants to change the data in the State, it must be done through Mution, which also provides the subscriber mode for external plug-ins to call to obtain the State data update. However, when all asynchronous operations (commonly used to call back-end interface to obtain update data asynchronously) or batch synchronous operations need to take Action, but Action can't directly modify State, it still needs to modify State data through Mutation. Finally, render to the view according to the change of State.

2. Briefly introduce the function of each module in the process

  • Vue Components: Vue Components. On the HTML page, it is responsible for receiving user actions and other interaction behaviors, and executing the dispatch method to trigger the corresponding action to respond.
  • dispatch: operation behavior trigger method is the only method that can execute action.
  • Actions: operation behavior processing module, which is composed of$ store.dispatch('action name', data1) to trigger. Then, commit() triggers the call of mutation to indirectly update the state. Responsible for handling all interaction received by Vue Components. It contains synchronous / asynchronous operations, supports multiple methods with the same name, and triggers in the order of registration. The operations requested from the background API are performed in this module, including triggering other actions and submitting the mutation. This module provides the encapsulation of Promise to support the chain triggering of action.
  • Commit: state change commit operation method. The only way to execute a mutation is to commit to it.
  • Transitions: state change action method, triggered by commit('mutation name ') in actions. Is the only recommended way for Vuex to modify state. This method can only synchronize, and the method name can only be globally unique. During the operation, some hook s will be exposed for state monitoring.
  • State: page state management container object. Centralized storage of scattered data of data objects in Vue components is unique globally for unified state management. The data needed 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 read method. This module is not listed separately in the figure and should be included in the render. Vue Components read the global state object through this method.

3.Vuex and localStorage

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

let defaultCity = "Shanghai"
try {   // The user has turned off the local storage function. At this time, add a try...catch in the outer layer
  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 to localStorage
      } catch (e) {}
    }
  }
})

  

It should be noted that in vuex, the saved states are all arrays, while localStorage only supports strings, so JSON conversion is needed:

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

  

summary

Common component communication scenarios can be divided into three categories:
1. Parent child communication
  • Data is passed from parent to child through props and from child to parent through events ($emit).
  • Communication is also possible through the parent / child chain ($parent / $children).
  • ref can also access component instances.
  • provide / inject API.
  • $attrs / $listeners.
2. Brother communication
  • Bus
  • Vuex
3. Cross level communication
  • Bus
  • Vuex
  • provide / inject API
  • $attrs / $listeners

Tags: Javascript Vue JSON Attribute

Posted on Wed, 20 May 2020 02:52:06 -0700 by oc1000