Communication between Vue components is what we often encounter in the project, and it is particularly important to choose the appropriate communication method. Here is a summary of the communication scheme used by the author in the actual project. If there is any omission, please forgive me. Article code details see DEMO; The article was first published on imondo.cn

Father and son components

Communication between parent and child components is common in Vue, and the key fields used are props and $emit.

Props to accept the parent component to subcomponents information fields, its type: Array < string > | Object; Refer to the documentation for detailed explanations

$EMIT propagates an event triggered by a child component up to a parent message.

Example:

// Parent

<template>
  <div class="parent"{{childMsg}}</p> <Child: MSG ="msg" @click="handleClick"/>
  </div>
</template>
<script>
import Child from "./Child";
export default {
  name: "Parent",
  components: {
    Child
  },
  data() {
    return {
      msg: "I want you to eat.",
      childMsg: ' '}; }, methods: {handleClick(val) {this.childmsg = val; }}}; </script> // Child <template> <div class="child"> < p > I am a child component < / p > < p > to the parent information: {{MSG}} < / p > < button @ click ="handleClick"</button> </div> </template> <script>export default {
  name: "Child", // Props: {MSG: String}, methods: {// Propagating event messages to the parenthandleClick() {
      this.$emit('click'.'I know.'); }}}; </script>Copy the code

The effect is as follows:

Component between the two generations

When we use props, we cannot pass data down to the infinite; provide/inject; Provide Provides data to descendant components, regardless of the depth of the descendant component. Inject data is available. Refer to the documentation for detailed explanations

Example:

// Grand
<template>
  <div class="grand"> < / p > < p > I am a grandfather "Parent" / > < / div > < / template > < script >export default {
  name: "Grand",
  provide: {
    grandMsg: 'Let's all eat.'
  },
  components: {
    Parent
  }
};
</script>

// Parent
<template>
  <div class="parent"< grandMsg}}</p> <Child /> </div> </template> <script> import Child from"./Child";
export default {
  name: "Parent",
  components: {
    Child
  },
  inject: {
    grandMsg: {
      default: ' '}}}; // Child <template> <div class="child"> < p > I am a child component < / p > < p > grandpa information: {{grandMsg}} < / p > < / div > < / template > < script >export default {
  name: "Child",
  inject: {
    grandMsg: {
      default: ' '}}}; </script>Copy the code

The effect is as follows:

Provide and Inject binding are not responsive. We can make data responsive by passing a grandparent instance of this or by using an Observable.

// Grand
<template>
  <div class="grand"> <p> I am grandfather </p> <inputtype="text" v-model="msg" placeholder="Enter grandpa's message."/>
    <Parent />
  </div>
</template>
<script>
import Parent from "./Parent";
export default {
  name: "Grand".provide() {
    return{// use function provide to return object grandVm: this // pass instance}; },...data() {
    return {
      msg: ""}; }}; </script> // Child <template> <div class="child"> < p > I'm a child component < / p > < p > grandpa instance information: {{grandVmMsg}} < / p > < / div > < / template > < script >export default {
  name: "Child",
  inject: {
    grandVm: {
      default: () => {
        "";
      }
    }
  },
  computed: {
    grandVmMsg() {
      returnthis.grandVm.msg; }}}; </script>Copy the code

The effect is as follows:

Use Observables to make an object responsive. It is used internally by Vue to process objects returned by the data function.

Example:

// Grand
provide() {
  this.read = Vue.observable({
    msg: ' '
  })
  return {
    read: this.read
  };
}
Copy the code

The effect is as follows:

Brother components

For the same level of communication between components, we can use EventBus or Vuex.

A simple EventBus example:

// Bus.js
import Vue from "vue";
export default new Vue();

// Child
<div class="child"> <p> I am a child of a </p> < button@click ="handleClick"</button> </div> <script> import Bus from"./Bus";
export default {
  name: "Child",
  methods: {
    handleClick() {
      Bus.$emit("click"."Hey, old iron."); }}}; </script> // ChildOne <div class="child"> <p> I am a child component two </p> <p> brothers call me: {{MSG}}</p> </div> <script> import Bus from"./Bus";
export default {
  name: "ChildOne".data() {
    return {
      msg: ""
    };
  },
  mounted() {
    Bus.$on("click", msg => { this.msg = msg; }); }}; </script>Copy the code

The effect is as follows:

v-modelwithsync

V-model is a common way to bind form values with ElementUI; We can directly modify the child component to modify the value passed in by the parent component, simplifying the logic of our component communication.

Example:

// ModelCom
<div class="child">
  <input type="text" @input="handleInput">
</div>
<script>
export default {
  name: "ModelSync"HandleInput (e) {const value = e.target.value; const value = e.target.value; this.$emit('input', value); }}}; </script> // Home <ModelSync v-model="msg"/>
Copy the code

The effect is as follows:

Sync modifier can also be bidirectionally bound to our prop.

It requires us to emit the this.$emit(‘ Update :prop’, val) event within the child component

// ModelCom
<input type="text" @input="handleChange">... props: ['value'], methods: { handleChange(e) { const value = e.target.value; // Trigger to update this.$emit('update:value', value);
  }
}

// Home
<ModelSync :value.sync="syncMsg"/>
Copy the code

The effect is as follows:

$childrenwith$parent

We can find the parent or child instances of each component by accessing $children and $parent of the component in the current instance object.

Example:

// Child
<div class="child"> < p > I am a child component < / p > < p > from the parent component MSG: {{MSG}} < / p > < / div >... <script>export default {
  name: "ChildParent".data() {
    return {
      value: ' '
    }
  },
  computed: {
    msg() {
      return this.$parent.value; }},created() {
    console.log(this.$parent); 
  }
}

// Parent
<input v-model="value" />

Copy the code

By entering values in the parent component, you can see that the child component data is updated at the same time

$attrswith$listeners

$attrs can pass features on components (except class and style) to internal components by v-bind=”$attrs”; The values passed in are related to the setting of inheritAttrs, which typically encapsulates advanced components.

When we use inheritAttrs to set true; [Fixed] Features written to the component are rendered when the component renders the DOM.

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 “.

The details can be seen in the document

Example:

// Attr
<div class="child"> <p>Attr</p> <p> This is$attrs: {{placeholder}}</p> <p> This is$listeners: {{test }}</p>
  <button @click="$listeners.click"> to monitor the$listeners</button>
</div>
...
<script>
export default {
  name: "AttrListen",
  inheritAttrs: true,
  props: {
    test: {
      type: String,
      default: ' '}},data() {
    return {
      placeholder: this.$attrs.placeholder
    }
  }
};
</script>

// Home
<AttrListen placeholder="This is an ATTR." :test="value" v-bind="$attrs" v-on="$listeners" @click="handleListen"/>

Copy the code

The effect is as follows:

Find components by encapsulation

Send events up or down by encapsulating functions

See vue.js for details on components

// emitter.js
function broadcast(componentName, eventName, params) {
    this.$children.forEach(child => {
        const name = child.$options.name;

        if (name === componentName) {
            child.$emit.apply(child, [eventName].concat(params));
        } else{ broadcast.apply(child, [componentName, eventName].concat([params])); }}); }export default {
    methods: {
        dispatch(componentName, eventName, params) {
            let parent = this.$parent || this.$root;
            let name = parent.$options.name;

            while(parent && (! name || name ! == componentName)) { parent = parent.$parent;

                if (parent) {
                    name = parent.$options.name; }}if (parent) {
                parent.$emit.apply(parent, [eventName].concat(params)); } }, broadcast(componentName, eventName, params) { broadcast.call(this, componentName, eventName, params); }}};Copy the code

Find any specified component by wrapping functions

See vue.js for details on components

// From a component, go up to the nearest specified componentfunction findComponentUpward (context, componentName) {
    let parent = context.$parent;
    let name = parent.$options.name;

    while(parent && (! name || [componentName].indexOf(name) < 0)) { parent = parent.$parent;
        if (parent) name = parent.$options.name;
    }
    return parent;
}
export{ findComponentUpward }; // From a component, find all specified components upfunction findComponentsUpward (context, componentName) {
    let parents = [];
    const parent = context.$parent;

    if (parent) {
        if (parent.$options.name === componentName) parents.push(parent);
        return parents.concat(findComponentsUpward(parent, componentName));
    } else {
        return[]; }}export{ findComponentsUpward }; // From a component, go down to all specified componentsfunction findComponentsDownward (context, componentName) {
    return context.$children.reduce((components, child) => {
        if (child.$options.name === componentName) components.push(child);
        const foundChilds = findComponentsDownward(child, componentName);
        returncomponents.concat(foundChilds); } []); }export{ findComponentsDownward }; // Find the sibling of the specified componentfunction findBrothersComponents (context, componentName, exceptMe = true) {
    let res = context.$parent.$children.filter(item => {
        return item.$options.name === componentName;
    });
    let index = res.findIndex(item => item._uid === context._uid);
    if (exceptMe) res.splice(index, 1);
    return res;
}
export { findBrothersComponents };
Copy the code

conclusion

The communication modes of the components in the project are generally used in the above schemes. We can realize component communication in different ways, but choosing the appropriate component communication mode can make us get twice the result with half the effort. Write improper place, hope corrects ~

Other summary articles:

  • Background management project summary
  • Webpack general packaging optimization

Welcome to pay attention to the public number, we communicate and progress together.