Before I wrote an article about vUE interview summary, there are a lot of netizens put forward that there are many ways to communicate between components, this article is dedicated to summarize the communication between components

Koala is dedicated to sharing the complete Node.js technology stack, from JavaScript to Node.js, to back-end database. Wish you become an excellent senior Node.js engineer. [Programmer growth refers to north] Author, Github blog open source project github.com/koala-codin…

Vue is a framework for data-driven view updates, so data communication between components is very important for VUE. How do components communicate with each other? First of all, we need to know what kind of relationship exists between components in VUE, so as to understand their communication mode more easily. It is just like going home for the New Year, sitting in a room full of strangers, how to call each other. At this time, we need to know what kind of relationship between ourselves and them. Description of relationships in VUE components:

As shown in the figure above, components A and B, A and C, B and D, and C and E are parent-child relationships; B and C are brothers. The relationship between A and D and between A and E is atavistic. D and E are Cousins (non-immediate relatives). For the above relations, we classify them as:

  • Communication between parent and child components
  • Communication between non-parent components (sibling components, intergenerational components, etc.)

This article will introduce 8 modes of communication between components as shown in the following figure: and introduce how to choose effective communication modes in different scenarios, hoping to help friends better understand communication between components.

A,props / $emit

The parent component passes data to the child component via props, while the $EMIT child component communicates to the parent.

1. The parent component passes the value to the child component

Section. Vue articles:[‘ DREAM of red Mansions ‘,’ Journey to the West ‘,’ Romance of The Three Kingdoms ‘]

// section parent component<template>
  <div class="section">
    <com-article :articles="articleList"></com-article>
  </div>
</template>

<script>
import comArticle from './test/article.vue'
export default {
  name: 'HelloWorld'.components: { comArticle },
  data() {
    return {
      articleList: [A Dream of Red Mansions.Journey to the West.Romance of The Three Kingdoms]}}}</script>

Copy the code
// Child component article.vue<template>
  <div>
    <span v-for="(item, index) in articles" :key="index">{{item}}</span>
  </div>
</template>

<script>
export default {
  props: ['articles']}</script>
Copy the code

Summary: PROP can only be passed from one level of component to the next level (parent and child components), so called one-way data flow. Prop is read-only and cannot be modified, and any changes are invalidated with warnings.

2. Child components transmit values to parent components

My own interpretation of $emit is that $emit binds a custom event, and when the statement is executed, the arG is passed to the parent component, which listens and receives the parameter via V-ON. An example shows how a child component passes data to its parent. Based on the previous example, click on the page rendered Ariticle item, the subscript shown in the array in the parent component

// In the parent component<template>
  <div class="section">
    <com-article :articles="articleList" @onEmitIndex="onEmitIndex"></com-article>
    <p>{{currentIndex}}</p>
  </div>
</template>

<script>
import comArticle from './test/article.vue'
export default {
  name: 'HelloWorld'.components: { comArticle },
  data() {
    return {
      currentIndex: - 1.articleList: [A Dream of Red Mansions.Journey to the West.Romance of The Three Kingdoms]}},methods: {
    onEmitIndex(idx) {
      this.currentIndex = idx
    }
  }
}
</script>
Copy the code
<template>
  <div>
    <div v-for="(item, index) in articles" :key="index" @click="emitIndex(index)">{{item}}</div>
  </div>
</template>

<script>
export default {
  props: ['articles'].methods: {
    emitIndex(index) {
      this.$emit('onEmitIndex', index)
    }
  }
}
</script>
Copy the code

Second,$children / $parent

vue
$parent
$children
data

Method of use

// In the parent component<template>
  <div class="hello_world">
    <div>{{msg}}</div>
    <com-a></com-a>
    <button @click="changeA">Click Change child component values</button>
  </div>
</template>

<script>
import ComA from './test/comA.vue'
export default {
  name: 'HelloWorld'.components: { ComA },
  data() {
    return {
      msg: 'Welcome'}},methods: {
    changeA() {
      // Get the child component A
      this.$children[0].messageA = 'this is new value'}}}</script>
Copy the code
// In the child component<template>
  <div class="com_a">
    <span>{{messageA}}</span>
    <p>Get the value of the parent component: {{parentVal}}</p>
  </div>
</template>

<script>
export default {
  data() {
    return {
      messageA: 'this is old'}},computed:{
    parentVal(){
      return this.$parent.msg; }}}</script>
Copy the code

Note the boundary cases, such as $parent on #app yields an instance of new Vue(), $parent on that instance yields undefined, and $children on the bottom child is an empty array. $parent is an array, and $children is an object

conclusion

The preceding two methods are used for communication between parent and child components, and it is more common to use props for communication between parent and child components. Neither can be used for communication between non-parent components.

Three,provide/ inject

Concept:

Provide/Inject is a new API in Vue2.2.0. Simply put, provide variables in the parent component and inject variables in the child component.

Note: As long as inject is called, it is possible to inject data in provide, regardless of how deeply nested the child component is, rather than being limited to returning data from the props property of the current parent component

For example verification

Let’s use an example to verify the above description: Suppose there are three components: A.vue, B.vue, and C.vue, where C is A child of B and B is A child of A

// A.vue

<template>
  <div>
	<comB></comB>
  </div>
</template>

<script>
  import comB from '.. /components/test/comB.vue'
  export default {
    name: "A".provide: {
      for: "demo"
    },
    components:{
      comB
    }
  }
</script>
Copy the code
// B.vue

<template>
  <div>
    {{demo}}
    <comC></comC>
  </div>
</template>

<script>
  import comC from '.. /components/test/comC.vue'
  export default {
    name: "B".inject: ['for'],
    data() {
      return {
        demo: this.for
      }
    },
    components: {
      comC
    }
  }
</script>
Copy the code
// C.vue
<template>
  <div>
    {{demo}}
  </div>
</template>

<script>
  export default {
    name: "C".inject: ['for'],
    data() {
      return {
        demo: this.for
      }
    }
  }
</script>
Copy the code

Four,ref / refs

Ref: 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, which can be used to call the component’s methods or access data directly. Let’s look at an example of a ref accessing a component:

// Subcomponent a.ue

export default {
  data () {
    return {
      name: 'Vue.js'}},methods: {
    sayHello () {
      console.log('hello')}}}Copy the code
// app.vue <template> <component-a ref="comA"></component-a> </template> <script> export default {mounted () {// app.vue <template> <component-a ref="comA"> const comA = this.$refs.comA; console.log(comA.name); // Vue.js comA.sayHello(); // hello } } </script>Copy the code

Five, the eventBus

EventBus, also known as the eventBus, can be used in vue as a bridge concept, as all components share the same event center to which they can register to send and receive events, so that components can notify other components.

EventBus also has its drawbacks. When a project is large, it can be a disaster that is difficult to maintain

How can YOU use eventBus to communicate data between components in a Vue project? Specifically through the following steps

1. Initialization

You first need to create an event bus and export it so that other modules can use it or listen on it.

// event-bus.js

import Vue from 'vue'
export const EventBus = new Vue()
Copy the code

2. Send events

Suppose you have two components: additionNum and showNum. These two components can be siblings or parent components. Here we take the sibling component as an example:

<template>
  <div>
    <show-num-com></show-num-com>
    <addition-num-com></addition-num-com>
  </div>
</template>

<script>
import showNumCom from './showNum.vue'
import additionNumCom from './additionNum.vue'
export default {
  components: { showNumCom, additionNumCom }
}
</script>

Copy the code
// addtionNum. Vue sends events<template>
  <div>
    <button @click="additionHandle">+ adder</button>    
  </div>
</template>

<script>
import {EventBus} from './event-bus.js'
console.log(EventBus)
export default {
  data(){
    return{
      num:1}},methods:{
    additionHandle(){
      EventBus.$emit('addition', {
        num:this.num++
      })
    }
  }
}
</script>
Copy the code

3. Receive events

// events are received in shownum. vue<template>
  <div>Count and: {{count}}</div>
</template>

<script>
import { EventBus } from './event-bus.js'
export default {
  data() {
    return {
      count: 0
    }
  },

  mounted() {
    EventBus.$on('addition', param => {
      this.count = this.count + param.num; }}})</script>
Copy the code

This makes it possible to click the add button in the component addtionNum. Vue and display the sum with the num passed in showNum. Vue.

4. Remove event listeners

If you want to remove listening for events, you can do something like this:

import { eventBus } from 'event-bus.js'
EventBus.$off('addition', {})
Copy the code

Six, Vuex

1. Vuex is introduced

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. Vuex solves the problem of multiple views relying on the same state and behavior from different views needing to change the same state, focusing developers’ efforts on updating data rather than transferring data between components

2. Vuex modules

  1. state: Is used to store data and is the only data source in the store
  2. getters: Secondary wrapping based on state data, like computational properties in VUE, is often used for filtering data and correlation calculation of multiple data
  3. mutations: function like, the only way to change state data, and cannot be used to handle asynchronous events
  4. actions: similar tomutation, for submissionmutationTo change state without directly changing state, and can include any asynchronous operation
  5. modules: similar to a namespace, used for projects to define and operate the state of each module separately for easy maintenance

3. Vuex example application

/ / the parent component<template>
  <div id="app">
    <ChildA/>
    <ChildB/>
  </div>
</template>

<script>
  import ChildA from './components/ChildA' // Import component A
  import ChildB from './components/ChildB' // Import component B

  export default {
    name: 'App'.components: {ChildA, ChildB} // Register components A and B
  }
</script>
Copy the code
// Child childA<template>
  <div id="childA">
    <h1>I'm component A</h1>
    <button @click="transform">Click I let component B receive the data</button>
    <p>Because you clicked B, my message has changed: {{BMessage}}</p>
  </div>
</template>

<script>
  export default {
    data() {
      return {
        AMessage: 'Hello, component B, this is component A '}},computed: {
      BMessage() {
        // This is where component B data is stored from the store
        return this.$store.state.BMsg
      }
    },
    methods: {
      transform() {
        // Trigger receiveAMsg and store the data of component A in the store
        this.$store.commit('receiveAMsg', {
          AMsg: this.AMessage
        })
      }
    }
  }
</script>
Copy the code
// Child component childB<template>
  <div id="childB">
    <h1>I'm component B</h1>
    <button @click="transform">Click I have component A receive data</button>
    <p>Because you clicked A, my information has changed: {{AMessage}}</p>
  </div>
</template>

<script>
  export default {
    data() {
      return {
        BMessage: 'Hello, component A, this is component B '}},computed: {
      AMessage() {
        // This is where the data of component A obtained from store is stored
        return this.$store.state.AMsg
      }
    },
    methods: {
      transform() {
        // Trigger receiveBMsg to store B component's data
        this.$store.commit('receiveBMsg', {
          BMsg: this.BMessage
        })
      }
    }
  }
</script>
Copy the code

Vuex store, js

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const state = {
  // Initialize the data of components A and B, and wait to obtain it
  AMsg: ' '.BMsg: ' '
}

const mutations = {
  receiveAMsg(state, payload) {
    // Store component A's data in state
    state.AMsg = payload.AMsg
  },
  receiveBMsg(state, payload) {
    // Store component B's data in state
    state.BMsg = payload.BMsg
  }
}

export default new Vuex.Store({
  state,
  mutations
})
Copy the code

Seven,localStorage / sessionStorage

This kind of communication is relatively simple, but the disadvantage is that the data and state are chaotic and not easy to maintain. Through the window. The localStorage. The getItem (key) to get the data through the window. The localStorage. SetItem (key, value) to store data

Json.parse ()/json.stringify () localStorage/sessionStorage can be combined with Vuex to persist data, and vuex can be used to solve the problem of data and state confusion.

eight$attrswith$listeners

Now let’s discuss A case where component A and component D in the component diagram we gave at the beginning were intergenerational. How did they communicate before?

  1. usepropsBinding to carry out the information transfer level by level. If the state change in component D needs to transfer data to A, the event system is used to transfer data level by level
  2. useeventBusIn this case, it is still better to use, but in the case of multi-team development, the code is low maintenance and low readability
  3. Use Vuex for data management, but if you’re just passing data around without intermediate processing, using Vuex feels like overkill.

In Vue2.4, $listeners and $attrs were introduced to address this requirement, and the inheritAttrs option was added. Prior to version 2.4, feature bindings (except for class and style) that were not recognized (and retrieved) as prop in the parent scope by default were “rolled back” and applied to the root element of the child component as plain HTML features. Let’s look at an example of cross-level communication:

// app.vue
// index.vue

<template>
  <div>
    <child-com1
      :name="name"
      :age="age"
      :gender="gender"
      :height="height"
      title="Programmer growth is north."
    ></child-com1>
  </div>
</template>
<script>
const childCom1 = (a)= > import("./childCom1.vue");
export default {
  components: { childCom1 },
  data() {
    return {
      name: "zhang".age: "18".gender: "Female".height: "158"}; }};</script>
Copy the code
// childCom1.vue

<template class="border">
  <div>
    <p>name: {{ name}}</p>
    <p>ChildCom1 $attrs: {{$attrs}}</p>
    <child-com2 v-bind="$attrs"></child-com2>
  </div>
</template>
<script>
const childCom2 = (a)= > import("./childCom2.vue");
export default {
  components: {
    childCom2
  },
  inheritAttrs: false.// You can turn off properties that are not declared in props that are automatically mounted to component root elements
  props: {
    name: String // Name is bound as the props property
  },
  created() {
    console.log(this.$attrs);
     / / {" age ", "18", "gender" : "female" and "height" : "158", "title" : "programmers growth refers to the north"}}};</script>
Copy the code
// childCom2.vue

<template>
  <div class="border">
    <p>age: {{ age}}</p>
    <p>childCom2: {{ $attrs }}</p>
  </div>
</template>
<script>

export default {
  inheritAttrs: false.props: {
    age: String
  },
  created() {
    console.log(this.$attrs); 
    / / {" gender ":" female "and" height ":" 158 ", "title" : "programmers growth refers to the north"}}};</script>
Copy the code

conclusion

Common usage scenarios can be divided into three categories:

  • Parent-child component communication:props; $parent / $children; provide / inject ; ref ; $attrs / $listeners
  • Sibling communication:eventBus ; vuex
  • Cross-level communication:eventBus; Vuex;provide / inject$attrs / $listeners

Today share so much, if you are interested in sharing the content, you can pay attention to the public account “programmer growth refers to north”, or join the technical exchange group, we discuss together.

Join us and learn!