Communication between components is a constant topic when writing Vue code. In daily development, the most common is the communication between parent and child components, in addition to parent and child components, as well as sibling components. The content of communication includes the transfer of data and the transfer of events, that is, to build Bridges between different components.

I mean, it sucks, but I don’t think it’s a very good one. There will be new updates to the article later.

Between father and sonpropsandemit

Props and emit props are arguably the most common ways for a parent component to pass data to a child component.

Prop is a set of custom attributes that you can register on components. When a value is passed to a prop attribute, it becomes a property of that component instance. By default, a component can have any number of props, and any value can be passed to any prop. Accessing props is just like accessing data in data. – Vue document

Here’s an example:

/ / child component
// Simple array of props
props: ['title'.'id']
// Props is a complex object that specifies the types, default values, and validation rules of props
props: {
  title: {
    type: 'String'.default: 'Article Title',
    validator (value) => {
      This value must match one of the following strings
      return value.length > 1 && value.length < 20}}}Copy the code
// Parent component <demo :title='diamond'></demo>Copy the code

Due to the restriction of one-way data flow in Vue, the child component cannot directly change the value in props.

All prop forms a one-way downlink binding between their parent prop: updates to the parent prop flow down to the child, but not the other way around. This prevents accidental changes in the state of the parent component from the child, which can make the data flow of your application difficult to understand. – Vue document

Note that objects and arrays are passed in by reference in JavaScript, so for a prop of an array or object type, changing the object or array itself in a child component will affect the state of the parent component.

But there will always be situations where you need to change the props. For example, if you have a popover component, the component provides isShow to control whether the popover is displayed or not, and the closing behavior is controlled from within the popover. What if you want to shut it down.

use$emitTell the parent to shut me down.

The $emit method can have multiple parameters, including the event name and the parameters to be passed. Any additional parameters will be passed to the listener callback. On the other hand, with the @ sign listening where the child component is used, the callback function receives all of the extra arguments that the incoming event triggers.

$fromChild= childEvent($event, $event1) = childEvent($event, $event1); Arguments (@fromChild=”childEvent(arguments, $event)”); arguments (@fromChild=”childEvent(arguments, $event)”); Specifically, you can see the reply of The University of Utah.

Add an event to the child component’s close button that tells the parent via $emit to close the popup. It worked, but it was clumsy.

<div> </ div> <span > </span> <button @click="hidden"> </ div> </template> <script> methods: { hidden() { this.$emit("hidden", false); } } </script>Copy the code
<DemoTest :isShow="show" @hidden="show = false"></DemoTest>
Copy the code

Added in 2.3.0updateandsyncThe modifier “silent” has changed its value.

<span > <button @click="hidden"> </span> </span> </button @click="hidden"> </span> </button @click="hidden"> {this.$emit("update:isShow", false); }Copy the code
// Parent component <DemoTest: isshow. sync="show"></DemoTest>Copy the code

The parent component is notified of the changes, albeit silently, rather than making them directly within the child component. The.sync modifier is essentially a syntactic sugar (something that makes you feel sweet), and the code that isn’t is as follows.

<DemoTest :isShow="show" @update:isShow="show = $event"></DemoTest>
Copy the code

Please note that

  1. V-bind with the.sync modifier cannot be used with expressions (e.g. V-bind :title.sync= “doc.title + ‘! ‘” is invalid). Instead, you can only provide the name of the property you want to bind to, similar to the V-Model.

  2. Using v-bind.sync on a literal object, such as v-bind.sync= “{title: doc.title}”, does not work because there are many edge cases to consider when parsing a complex expression like this. : : :

In this scenario, isShow is inter-referenced. If it is not inter-referenced, and you need to modify the value of props inside the component, you can declare a computed or data inside the child component to do so.

Between father and son$parent,childrenand$refs

The $parent Property can be used to access an instance of the parent component from a child component. It provides an opportunity to reach the parent component at any time later, instead of passing data to the child component as a prop.

For example, in the popover scenario, it can be used directly in child componentsparent.show = false. However, directly changing the data in the parent component in the child component is not recommended, or not very common. Again, in the case of the popover component, in the other parent component, itsdataValue may not beShow ‘is something else, in which case the pop-up window component’s closing method will fail.

Therefore, using the $parent attribute to directly change the value of the parent component attribute is not reliable. Even if it is used, it is recommended that it be used only in non-public components that are simply removed for code cleanliness.

$childrenProperty gets the immediate child components of the current instance as an array, ** needs to be notedThere is not necessarily only one child component in the children ‘attribute. It is not convenient to obtain a large number of child components using this attribute.

The $refs object holds all DOM elements and component instances registered with refAttributes. This object is more powerful than either of the above in that, with the exception of the component, it can also get DOM elements. In contrast to the $children attribute, the $refs object is explicit about the child components that need to be retrieved, because the specific ref needs to be specified in the components that use the object. With $refs, you can get almost any property or method on the corresponding component.

Note that $refs only takes effect after the component is rendered, and they are not reactive. This is only used as an “escape hatch” for direct manipulation of child components — you should avoid accessing $refs in templates or computed properties.

If the v-if state of the component is false, the value of an existing ref can be retrieved only when the component is mounted.

EventBus

Parent parent and child parent can be implemented using props and Event respectively. However, it would be cumbersome to still use this method in sibling components. After all, implementations of props and Events need to be parent-child in their code structure, not sibling components. It can be implemented by a parent component, but only in the case of a small number of hierarchies; it is much more difficult if it is a descendant component of a deeper level.

As shown in the figure below, it is easy to implement functions and Events for the root component to communicate with subcomponent AB, but it is not easy to implement the communication between subcomponent A and subcomponent B, or even the communication between grandchild component C and grandchild component D.

At this time, we need to introduce the concept of an event busEventBus. I think English is much easier to understand than the word event bus. In plain English, there is a bus in each component (which needs to be introduced by the component itself), and the component can let the bus tell it if it needs to know or publish information (Emit).

Before you can use it, you need to create an interface to host event listening and publishing, essentially creating an empty Vue instance.

import Vue from 'vue'
let eventBus = new Vue()
export default eventBus
Copy the code

Why is it possible to send and listen to events by creating a new Vue implementation? $emit $on $OFF can be used to emit, listen on, and cancel listening events. It is possible to use $on within a single component to listen for events within the component, but it is usually triggered directly by related events, so it is less likely to be used within a single component.

As you can see from the documentation, both $emit and $ON exist as methods on instances, so it is natural to listen for events on one instance. Also, share a small problem I encountered here. Can $on listen for emit events from child components? The answer is no, because this is not the same.

It says in the documentationEmit, the callback function will receive all the additional arguments that are passed to the event-firing function. You need to make sureOnly if the instance of ON is the same instance, can the corresponding event trigger be listened to. If you need to trigger an event in a child component, you can do so byThe parent. $emit (‘ test ‘, ‘test’) `.

You need to import the file that instantiates eventBus in the child component, and then write the code directly in Mounted or Mounted. Refer to the code for details

// Publish events
methods: {
    testClick() {
      eventBus.$emit("helloFromApp"."From the test"); }}// Listen on events
mounted() {
    eventBus.$on("helloFromApp", e => {
      console.log(e);
    });
}
Copy the code

But here’s a caveat:

  1. Events on the file eventBus are not destroyed with the destruction of the component
  2. EventBus allows events with the same name to exist, that is, not be overwritten
  3. Once the component is recreated, the registration event is bound again

So you need to clean up the battlefield while destroying the component, and use $off to remove the listening event.

beforeDestroy () {
    eventBus.$off('helloFromApp')}Copy the code

When removing event listeners:

  1. If no arguments are provided, all event listeners are removed
  2. If only events are provided, all listeners for that event are removed
  3. If both an event and a callback are provided, only the listener for that callback is removed

The application we just made to eventBus was to create a separate file for it. However, you can also create a global eventBus directly on main.js to add to the instance prototype.

var EventBus = new Vue();

Object.defineProperties(Vue.prototype, {
    $bus: {
        get: function () {
            return EventBus
        }
    }
})
Copy the code

Question, why all on one instance, child componentsOn is listening.

provideandinject

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 as long as the upstream and downstream relationship is established. 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 whose key is the local binding name.

::: Warning provide and Inject binding are not responsible, this is intentional. However, if you pass in a listening object, the object’s property is still responsive. : : :

After 2.2.1, injected values can be used as the default values in props or data. In addition, in versions 2.5.0 and above, you can even set default values for injection.

const Child = {
  inject: ['foo'].// The default value
  inject: {
    foo: {default: 'foo'}
  }
  props: {
    bar: {
      default () {
        return this.foo  
      }
    }
  },
  data () {
    return {
      bar: this.foo
    }
  }
}
Copy the code

But if you just use provide down to provide some data for its descendant components, it’s still a bit of a drag. At this point, you can use the eventBus concept mentioned earlier directly in Provide. Since the value provided needs to be an object, turn the value into an eventBus, and descendant components inject Inject to happily use eventBus.

data () {
  return {
    eventBus: new Vue()
  }
},
provide () {
  return {
    eventBus: this.eventBus
  }
}
Copy the code

Provide, Inject and EventBus:

  1. EventBus implements component communication without providing or Injecting. The principle is implemented internally by vUE$emit $on $offEvent mechanism.
  2. Provide and inject facilitate communication among components, but the direction is single and data form is single, that is, only ancestor components can provide data downward.
  3. The combination of provide, Inject, and eventBus can better realize communication between components.

$attrsand$listeners

As mentioned earlier, we used props to specify the parameters needed to use the current component and emit to expose the events of the current component.

But when a child component contains a component, the attributes passed in by the child component and the events that use the child component should be written in the child component. $listeners and $listeners For example, many small factory in-house component libraries have components packaged based on elementUI, as follows.

<template>
  <el-radio v-bind="$attrs"
            v-on="$listeners">
    <slot></slot>
  </el-radio>
</template>
Copy the code

$listener and $attrs are used to pass parameters to el-Radio that are used by the parent component without writing any props or events.

Note, however, that class and style are not passed to the grandchild component.

vuex

All of this is component-level communication, but When it comes to intra-project level global communication, eventBus is a little out of its depth. That’s where Vuex comes in.

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.

Several articles could be written on the use and principles of Vuex, but I will not cover them here.