We all know that Vue is a lightweight, low-entry front-end framework whose core is componentized development. Vue is composed of multiple components, componentization is its essence, but also its strength. Component instances are independent of each other, which means that data cannot be shared between different components.

However, in the actual project development, we often need the data of other components, so the problem of component communication is born. For the relationship between components before: father and son, brother and descendants of different data transmission methods are not the same, today to make a summary.

Vue2 component communication

Parent component passes value to child component

props

  • The parent component passes values to the children as properties
  • Subcomponents receive data in props mode

Introduce the child component in the parent and bind the fatherData custom property

<Son :fatherData="fatherData"></Son> <script> import Son from '@/components/son' export default{ name:'Father', Components :{Son}, data(){return{fatherData:' I'm props'}}} </script>Copy the code

In the child component, props is used to receive data from the parent component. The props name is the same as the property name defined by the parent component

<template> <div> I am the parent of the data: {{fatherData}}</div> {{mydata}}</div> </template> <script> export default{ name:'Son', props:{ fatherData:{ type:String, Mydata (){mydata:' fatherData '+ this.fatherdata}, watch:{fatherData(newVal){this.mydata=' fatherData ' + newVal}},} </script>Copy the code

Because of Vue’s one-way data flow mechanism, a child component cannot directly modify the value passed by the parent component, otherwise the parent component’s value will be “contaminated”.

If a child component wants to modify the value passed by the parent component without contaminate the parent component, it can define a variable myData to receive fatherData data and use watch to listen for changes to fatherData data.

Child components pass values to parent components

$emit()

  • Child components bind custom events
  • Use $emit() to trigger changes to the data
<el-button @click="handleEmit"> </el-button> <script> export default{name:'Son', Methods :{handleEmit(){this.$emit('triggerEmit',' I am data from a child component ')}}} </script>Copy the code
  • The parent component defines and binds triggerEmit events passed by the child component
  • The triggerEmit event name must match the event name of the child component $emit()
<Son @triggerEmit="changeData"></Son> <script> import Son from '@/components/son' export default{ name:'Father', Components :{Son}, methods:{changeData(name){console.log(name) // => I am data from the child component}}} </script>Copy the code

Usage scenario: When a generic component changes internal data, it informs the parent component of the current data state.

Brother prequel value

$emitandpropsWay to combine

  • The parent component introduces two child components
  • The parent component acts as a bridge
<childA :myName="name"></ChildA> <ChildB :myName="name" @changeName="editName"></ChildB> export default{ data() { return {editName(name){this.name = name}}}Copy the code

Receive variables and binding trigger events in child component B

<p> name: {{myName}}</p> <button @click="changeName"> </button> <script> export default{props: {myName:String}, methods: {changeName() {this.$emit('changeName', 'public id: initial ')}}} </script>Copy the code

When the parent component receives the changeName event from component B, it executes the editName event and modifies the name data. Using A reactive mechanism, the parent component passes the latest name value to component A, which renders the latest name value

<p> name: {{newName}}</p> <script> export default{myName:String}}</ script>Copy the code

So far, component B’s changes are passed through the parent to component A to complete the data transfer

Bus way

  • Create a public bus.js file
  • The Vue instance is exposed
import Vue from "vue"
export default new Vue()
Copy the code

This file is introduced in components that require component communication

<template> <div> <div> I am A communication component A</div> <el-button @click="changeName"> </el-button> </div> </template <script> import { EventBus } from ".. /bus.js" export default{data(){return{}}, methods:{changeName(){EventBus.$emit("editName") ') } } } </script>Copy the code

Another component also introduces a bus.js file that listens for event callbacks via $on

<template> <div> I am communication component B</div> </template <script> import {EventBus} from ".. /bus.js" export default{data(){return{}}, mounted:{EventBus.$on('editName',(name)=>{console.log(name) // => Pay attention! }) } } </script>Copy the code

Making EventBus get the Vue instance by importing the bus.js file is the same as the previous method.

Direct access to the component instance

parent/children

  • The child gets the parent instance through $parent
  • The parent component gets an array of child component instances through $children
<template> <div> <script> export default{name:"Son", data(){return{sonTitle: }}, methods:{sonHandle(){console.log(' I am the child's method ')}}, Created (){console.log(this.$parent) console.log(this.$parent.fathertitle) // => I am the data of the parent component this.$parent.fantherHandle() // => I am the parent component of the method}} </script>Copy the code

$parent: $parent: $parent: $parent: $parent: $parent: $parent: $parent: $parent: $parent: $parent: $parent: $parent: $parent

<template> <div> </template> <script> import Son from './son.vue' export default{name: 'father', components:{ Son }, data(){ return{ fatherTitle: }}, methods:{fantherHandle(){console.log(' I am the parent's method ')}}, // echo (){console.log(this.$children[0]. SonTitle) console.log(this.$children[0] This.$children[0]. SonHandle () // => I am a child method}} </script>Copy the code

As you can see, the parent component retrieves child instances in the Mounted () life cycle in the form of an array

So we need this.$children[0] to get a component instance and call component methods and data

$refs

  • The parent component uses $refs to get the component instance
<template> <div> <Son ref="son"></Son> </div> </template> <script> import Son from './son.vue' export default{ name: 'father', Components :{Son}, mounted(){console.log(this.$refs.son) / /Copy the code

The parent component can then use this.$refs.xx directly to get an instance of the child component

Multi-component or deep component communication

Provide/provide/inject

  • The parent component uses provide to inject data
  • Subcomponents use Inject data

For example, if we have a hierarchy like this:

Then our provide/inject will come in handy

/* Parent component */ export default{provide: {return{provideName: 'primary'}}Copy the code

So far, the variable provideName can be provided to all its sub-components, including great-grandson and great-grandson components, etc. Only inject is needed to obtain data

/* Sub-component */ export default{inject: ['provideName'], created () {console.log(this.providename) // => "primary front-end"}}Copy the code

The benefits of this approach are:

  • The parent component does not need to know which component uses the data it provides
  • Child attachments don’t need to know where this data comes from, right

Vuex status management

  • It acts as a repository of common data
  • Provides ways to manage warehouse data

For this, go directly to vuex[vuex.vuejs.org/zh]


conclusion

So far, the communication modes of vuE2 components are summarized, which can be divided into three categories:

Parent /child communication: the parent passes data to the child via props, the child passes data to the parent via $emit events, the parent/child chain uses parent/children, and accesses component instances directly using refs

Brother communication: Bus, Vuex

. Cross-level communication: bus, Vuex, provide/ Inject

Vue3 component communication

Props and emit

The setup function can take two arguments, prop and context, where context can be deconstructed to emit an instance

<template> <el-button @click="handle"> </el-button> <div> {{name}}</div> </template> <script> export default { name:"Son", props: { name: { type: String, default: '' } }, setup(props,{ emit }) { console.log(props.name) // => "lalal" function handle() { emit('handleClick', 'Vue3 awesome ')} return {handle}}} </script>Copy the code

Vue3 does not have the concept of this anymore, so this.$emit does not exist, so you can emit an emit instance from the context structure passed in by setup and send events to the parent component

<template>
  <Test name="lalal" @handleClick="myClick">点击</Test>
</template>

<script>
import Test from './index.vue'
export default {
  name:"father",
  components: { Test },
  setup() {
    function myClick(name) {
      console.log(name)
    }
    return {
      myClick
    }
  }
}
</script>
Copy the code

ref

Vue3 We can export the REF method from Vue to get instances of the child components

<template> <Test ref="btnRef"> <el-button @click="click"> </el-button> </template> <script> import {ref} from "vue" import Test from './index.vue' export default { components: {Test}, setup() { const btnRef = ref(null) function click() { btnRef.value? .sendparent () // => I am calling the method to the parent component} return {btnRef, click}}} </script>Copy the code

Const btnRef = ref(null) const btnRef = ref(null) const btnRef = ref(null) const btnRef (null

The btnref.value used here? Is the optional chain operator syntax, which stands for? If the value of the preceding statement is true, continue to execute the following statement. If you are interested, you can click to see it

<template> <slot></slot> </template> <script> export default { setup() { function sendParent() { Console. log(" I am the method called to the parent component ")} return {sendParent}} </script>Copy the code

The child component provides only one method that the parent component can execute after acquiring the instance, or it can perform more complex data communication

provide/inject

The usage here is not very different from Vue2, go directly to 🌰

<template> <Test></Test> </template> <script> import { provide } from "vue" import Test from './index.vue' export Default {components: {Test}, setup() {provide('fromFather', 'fromFather') return {}}} </script>Copy the code

The data that needs to be passed is injected directly using provide, regardless of who gets it

<template> <slot></slot> <div> I am the data injected by the parent component: {{fatherData}}</div> </template> <script> import {inject} from "vue" export default {setup() {// I go to the parent component to inject data let fatherData = inject('fromFather') return { fatherData } } } </script>Copy the code

The descendant component uses Inject to get data injected by the parent component


Thank you