preface

The scope of Vue component instances is independent of each other, and usually a page is composed of many components, which may be nested with components, forming a network diagram. Their relationship can be roughly divided into two usage scenarios, communication between parent components and communication between non-parent components, as shown in the following figure. Communication between parent and child components is divided into direct parent and indirect parent relationship. Vue provides a variety of communication methods, and choosing the most appropriate communication method for different communication needs can help us improve development efficiency. This paper briefly introduces nine communication methods and applicable scenarios, and records the receipt of goods in learning.

Communication between parent and child components

Props and $emit

The parent component passes in the parameter via V-bind, the child component receives the parameter via props, the child component passes in the event name via the built-in $emit method to trigger an event, and the parent component listens for the event via V-ON as if it were listening for a native DOM event.

// CompFather <template> <div> <comp-son :title-name="title_name" @changetitle ="changeTitle" /> <div> {{ title_name }}</div> </div> </template> <script> import CompSon from "./CompSon"; Export default {name: "CompFather", components: {CompSon}, data() {return {title_name: "I am the initial value"}}, methods: { changeTitle(val) { this.title_name = val; } } } </script>Copy the code
CompSon <template> <div> <div> titleName: {{titleName}}</div> < button@click ="changeTitle"> change </button> </div> </template> <script> export default {name: "CompSon", props: { titleName: { type: String, required: true } }, methods: {changeTitle() {this.$emit("changeTitle", "I change "); } } } </script>Copy the code

Application scenario: This mode is used for communication between components that have direct parent-child relationship (without nested components).

$attrsand$listeners

$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 the internal component can be passed in via v-bind=”$attrs”.

// Comp1.vue
<template>
   <comp2
      :flag="flag"
      :id="id"
      :msg="msg"
   />
</template>
​
<script>
   import Comp2 from "./comp2";
   export default {
      name: "comp1",
      components: {Comp2},
      data() {
         return {
            id: 1,
            msg: "comp1's msg",
            flag: true
         }
      }
   }
</script>
Copy the code
Vue <template> <div> <div> Comp2 $attrs: {{$attrs}}</div> <comp3 v-bind="$attrs" /> </div> </template> <script> import Comp3 from "./comp3"; export default { name: "comp2", components: {Comp3}, props: { flag: Boolean }, mounted() { console.log(this.$attrs); // { "id": 1, "msg": "comp1's msg" } } } </script>Copy the code
// Comp3.vue
<template>
   <div>comp3的$attrs:{{ $attrs }}</div>
</template>
​
<script>
   export default {
      name: "comp3",
      props: {
         msg: String
      },
      mounted() {
         console.log(this.$attrs);  // { "id": 1 }
      }
   }
</script>
Copy the code

Comp2 props received a flag from Comp1, so $attrs in Comp2 has no flag, Comp3 received MSG, so $attrs has id.

Application scenario: The $attrs attribute can be used for cross-level parameter transfer between components, which makes code simpler and easier to maintain.

The $Listeners contain V-ON event listeners in the parent scope (without the.native modifier). It can be passed into internal components via V-on =”$Listeners “.

When we want to encapsulate an input component, we can listen for its native event in a. Native way.

// App.vue
<my-input @input.native="inputData" :value="value" />
Copy the code
// MyInput.vue
<template>
   <input
         type="text"
         :value="value"
         @input="$emit('input',e.target.value)"
      >
</template>
​
<script>
   export default {
      name: "MyInput",
      props: {
         value: String
      },
   }
</script>
Copy the code

Native listeners will be disabled if the input is no longer the root component, so vue provides $Listeners to solve this problem.

// MyInput. Vue <template> <label> value: {{ value }} <input type="text" :value="value" @input="$emit('input',e.target.value)" > </label> </template>Copy the code

The following are listeners using the $Listeners attribute:

// App.vue <template> <my-input v-model="value" @focus="onFocus" @input="inputData" /> </template> <script> export Default {name: "App", data() {value: ""}, onFocus() {console.log(" get focus "); }, inputData(val) { console.log(val); } } </script>Copy the code
< the template > < label > value: {{ value }} <input type="text" :value="value" v-on="inputListeners" > </label> </template> <script> export default { name: "MyInput", props: { value: String }, computed: { inputListeners: Return object.assign ({}, const vm = this // 'object.assign' // We add all listeners from the parent this.$listeners, // then we add custom listeners, // or overwrite some listener behavior {// here make sure the component works with the work input of 'v-model' : function (event) { vm.$emit('input', event.target.value) } } ) } }, } </script>Copy the code

Note: Native listeners can be used to listen for native event listeners of subcomponents, which may be refactoring components. Native listeners are not native listeners, but there are other ways to do this.

$children/$parent/$root

$children is a direct child of the current instance, and the order is not guaranteed. Array order is not necessarily the order in which children are rendered in the parent, nor is it reactive.

If the current instance has a parent, $parent is the parent of the current instance.

$root is the root Vue instance of the current component tree. If the current instance has no parent, the instance will be itself. Access it via this.$root.

<script>
   export default {
      name: "MyComp",
      data() {
          value: ""
      },
      mounted: {
        console.log(this.$parent);
        console.log(this.$children);
        console.log(this.$root);
      }
   }
</script>
Copy the code

Disadvantages: $children gets instances in an unordered order, so if there are multiple child components it may not get the one it wants. $parent.$parent.$parent… Is not friendly for subsequent maintenance.

$parent/$chilren: $parent/$chilren

ref

Ref is used to register reference information for an element or child component. The reference information will be registered with the parent component’s $refs object. If used on a normal DOM element, the reference refers to the DOM element. If used on a child component, the reference refers to the component instance.

// App.vue <template> <comp1 ref="comp1"/> </template> <script> export default { name: "App", data() { value: }, mounted: {console.log("comp1 MSG: ",this. new); } } </script>Copy the code

Application scenario: When using an Element-UI component, it can be used to call component methods, such as selecting table items, sorting, and so on, in the EL-Table component.

v-model

By default, a V-Model on a component makes use of a prop named Value and an event named input, but input controls of types like checkboxes, checkboxes, and so on May use value attributes for different purposes. The Model option can be used to avoid such conflicts.

// App.vue
<add-item v-model="input_val" />
Copy the code
<template> <div class="add"> <input :value="value" type="text" placeholder="What needs to be done?" @input="inputVal" > </div> </template> <script> export default { name: "AddItem", // If the corresponding props field name is not value, then you need to define the model property to specify the v-Modal binding value for the parent component /*model: {prop: "value1", event: "input" },*/ props: { value: { type: String, required: true } }, methods: { inputVal(e) { this.$emit("input", e.target.value); } } } </script>Copy the code

Application scenario: V-Model is used to bind data bidirectionally when encapsulating input components. If you do not use v-Model, you need to add a method in the parent component to listen for events and then change the value in the parent component, which is very cumbersome and not concise.

Sync modifier

Sync is very similar to v-Model in that it is appropriate when a child component needs to change the value of the parent component. Update :isShow=” BOL =>isShow= BOL

<template> <div> <input type="button" value=" show"> <child :isShow. Sync ="isShow" v-show="isShow"/> </div> </template> <script> import child from "@/components/child" export default { data() { return { isShow:false } }, components:{ child }, methods:{ show(){ this.isShow=true; }, changeIsShow(bol){ this.isShow=bol; } } } </script>Copy the code
<template> <div> I'm a child component, I'm in a sea of red! </div> </template> <script> export default {methods:{upIsShow(){methods:{upIsShow(){ this.$emit("update:isShow",false); } } } </script>Copy the code

provide/inject

This pair of options needs to be used together to allow an ancestor component to inject a dependency into all of its descendants, regardless of how deep the component hierarchy is, and remain in effect for as long as its upstream and downstream relationships are established.

Provide (props) throws data to be used by the child component, and then inject data to be used by the child component. Unlike props, which need to be passed layer by layer, provide and inject data in the child component. It is mainly used in higher-level component libraries, but is generally not applicable in normal development because it is not convenient to trace the “source” and do not know which layer is declared and used.

The provide in the descendant overrides the property values of the same key in the grandparent.

The provide option should be an object or a function that returns an object. This object contains properties that can be injected into its descendants.

The inject option should be an array of strings or an object, from is the key selected from which value the grandfather component provides, and default specifies a default value.

// Parent component provide: ["message"], // child component inject: {MSG: {from: "message", default: "default value"}}Copy the code
// the parent component CompFather <template> <div> <comp-son /> </div> </template> <script> import CompSon from "./CompSon"; Export default {name: "CompFather", components: {CompSon}, provide: {MSG: "parent component MSG"}} </script>Copy the code
// subcomponent CompSon <template> <div> Subcomponent </div> </template> <script> export default {name: "CompSon", inject: ["msg"], mounted() { console.log(this.msg); MSG}} </script>Copy the code

Provide and Inject binding are not responsive. This is intentional. However, if you pass in a listening object, the object’s property is still responsive. For example, provide and Inject are an object, the data is responsive.

// When provide/inject is an object, it is responsible. If the child component changes the value, the parent component changes, and the parent component changes the child component. This.msg}, data() {return {MSG: {title: "parent component MSG"}}Copy the code

Application scenario: It is used to encapsulate high-order components. The ancestor instance does not care about which descendant instance will use it, and the descendant instance does not care about which ancestor instance data comes from.

Communication between non-parent and child components

Central event bus

Using the central event bus is essentially creating a VUE instance that is used to pass messages.

Use mode 1:

// define a bus file import Vue from "Vue "; const bus = new Vue(); export default bus;Copy the code
Import bus from "@/bus"; bus.$emit("myEvent", "bus msg")Copy the code
Import bus from "@/bus"; bus.$on("myEvent", data => { console.log(data); });Copy the code

Mode of use 2:

// main.js
import Vue from 'vue'
import App from './App.vue'
​
Vue.prototype.$bus = new Vue();
​
new Vue({
  render: h => h(App),
}).$mount('#app')
​
Copy the code
This.$bus.$emit("myEvent", "bus MSG "); $bus.$on("myEvent", data => {console.log(data); })Copy the code

Application scenario: It is suitable for communication between cross-level and cross-sibling components in a single page application that is not particularly large.

VueX

When we are developing large applications, but single page between components need to frequently used to modify certain values, this time using the component between the chaos is very multifarious, and not conducive to maintenance management, provides VueX vue in order to solve this problem the state management tool, we only need to put more components need Shared state VueX of unified management, Form a global singleton management mode to realize component data synchronization.

The state of VueX to be managed is placed in state, so that the state can be obtained in the page that references VueX. Actions are mainly used to handle asynchronous operations such as background requests to ensure data synchronization. Methods in Actions are called through dispatch method. When the asynchronous data is obtained, the method in mutations is called by commit to change the state. Do not directly modify state data in the component via $store.state, which is not monitored by Devtools, but rather mutations.

Here is a brief introduction to the communication mode, see detailed usage

[VueX official website] vuex.vuejs.org/zh/

conclusion

Communication mode Applicable scenario
props/$emit Direct parent component value transfer
$attrs/$listeners $attrsIndirect transmission between parent and child components,$listenersListening for native events
$children/$parent It is usually replaced by other methods, which is not convenient to maintain
ref Can be used to invoke higher-order component methods, such as registrationelement-uiA component reference
v-model Used to encapsulate components that require bidirectional bindingv-modalTo preach the cords
syncThe modifier Used between parent and child components when the child component needs to change the value of the parent component
provide/inject It is mainly used for higher-order component library, which is not used in normal development and is not conducive to code maintenance
Central event bus Suitable for cross – level or sibling communication.
VueX Used when multiple components in a large single page need to share the same state.