Communication between Vue components
When we are developing a project based on Vue, data communication between components is something we must consider.
**Note: **
The implementation method in this article is to implement it without considering
vuex
. And only tovue@2
, invue@3
, there are other better implementation solutions.
I roughly divide the relationship between components into three types:
- Parent-child component
<parent>
<child></child>
</parent>
Having a similar structure, the parent
component contains the child
component, then the child
component is the child component of the parent
component, and the parent
component is the parent component of the child
component.
- Brother components
<item></item> <item></item>
The two item
components are structurally the same level, and we call them brother components.
- Cross-multi-level components
<list>
<item>
<message><message>
</item>
</list>
<dialog>
<content></content>
</dialog>
In this structure, <list>
and <message>
are not direct parent-child components, and there is also a level in between. In actual scenarios, there will be component relationships across more levels. The <message>
and <content>
components are neither brother components nor father-son components, but cross the multi-level relationship between brothers and father-sons, and there will also be interactions in actual scenarios.
So how should we communicate with components of these three relationships?
Parent-child component communication
To talk about the communication between parent and child components, first of all, we need to understand the characteristics of the vue
component.
- One-way data stream, data from top to bottom.
Prop is one-way bound: when the property of the parent component changes, it will be passed to the child component, but the other way around will not. This is for To prevent child components from accidentally modifying the state of the parent component to prevent the application's data flow from becoming difficult to understand.
- Events come from bottom to top.
The changes in the internal state of the component bubble upwards through events, notifying the previous component, the previous component listens to the event and triggers the corresponding callback.
Based on the above, the recommended way of communication between parent and child components is:
The parent component passes the state to the child component through props
. The child component bubbles the state to the parent component through events. The parent component listens to trigger the callback to change the state.
parent.vue
<template>
<div class="parent">
<child :name="name" @name-change="nameChange"> </child>
</div>
</template>
<script>
import Child from './child';
export default {
name: 'parent',
data () {
return {
name: 'Jack'
};
}
methods: {
nameChange(name) {
this.name = name;
}
},
components: {
Child
}
}
</script>
child.vue
<template>
<div class="child">
<span>{{name}}</span>
<button @click="onClick">change name</button>
</div>
</template>
<script>
export default {
name: 'child',
props: {
name: {
type: String,
defualt() {
return ''
},
},
},
methods: {
onClick() {
this.$emit('name-change', 'John')
},
},
}
</script>
In some examples or personal projects, it is often found that using this.$parent
in child components directly changes the state of the parent component. It is true that this method can simplify the data communication between two deeply coupled components, and it will be more convenient in some simple scenarios. However, this method is not recommended to implement parent-child component communication. The consequence of this is that it leads to the uncertainty of the data flow, sacrificing the simplicity of the single data flow, and the data changes and flow become difficult to understand.
Brother component communication, cross-multi-level component communication
There is no direct connection between these two components.
For example, if a brother component, we will naturally think of using their parent component as a relay to communicate the state of child component 1
to the parent component, and then the parent component flows to child component 2
through props
, and vice versa. However, if the interaction between the brother components is complex but there is no direct interaction correlation with the parent component, the parent component assumes unnecessary responsibilities in this process.
Another example is across multiple levels of components. In the above example, there are multiple layers between <list>
and <message>
. If we continue to use parent-child components to communicate prop
and event bubbles, the intermediate layers need to repeatedly define prop
and events, which obviously also leads them to assume unnecessary responsibilities. There is no structural correlation between the <message>
and <content>
components, and the prop
and event bubbles seem very weak and cannot directly complete communication.
So how can we complete communication between these two components without causing unnecessary interference to their intermediate hierarchical components or parent components?
Since the two component relationships are not directly related, we need to have a bridge that can directly connect them to make them related. That is, we need a middleware
.
The solution given to us is vuex
, but I think it is more of a global state management. Using it as a communication middleware for some two components seems to be overused, so I won't discuss it here.
The solution I took is to complete component communication using custom events.
Instantiated Vue:
vue
has implemented an event system, which can be used to easily complete our component communication.
let middleware = new Vue();
export defend middleware;
message.vue
export default {
name: 'message',
data() {
return {
info: 'hello',
}
},
methods: {
sayHello() {
middleware.$emit('say-hello', this.info)
},
},
}
content.vue
export default {
name: 'content',
data() {
return {
info: '';
}
},
created() {
middleware.$on('say-hello', info => {
this.info = info;
});
}
}
We register the say-hello
event in content.vue
through middleware
. When message.vue
triggers the event, content.vue
listens to the event triggering the callback, thus realizing state conduction.
Component data transmission is no longer transmitted through props
, but communicates through events.
**If we do not use the instantiation of Vue to complete it, we can also implement a set of custom events ourselves. ** This can make more personalized custom events to meet the diverse usage scenarios in the project.
class Event {
constructor() {
// some props
}
on() {
// do something
}
emit() {
// do something
}
off() {
// do somethig
}
}
Summarize
Component communication of complex structures, and the key to implementing their communication is to implement middleware as a bridge to connect them, whether using custom events or other solutions.