Through reading official documents and a large number of netizens’ blog posts, I integrated my own ideas, sorted out my views on component communication, and posted a document for the first time. Please forgive me if there is something wrong
The reason for communication between components
Component instances are isolated in scope, but relationships and interactions between components are unavoidable. Component communication is essential when components interact with each other.
The type of component communication
Thus, component communication can be divided into two cases:
Communication between parent and child components (parent to child, child to parent), communication between non-parent components.
Parent to child type
Prop the value
Step1: the parent component passes properties with prop custom properties, which have literal and dynamic syntax. Literals can only pass data as strings; Dynamic syntax is similar to V-bind in that it can pass real-time changes in parent component data to child components.
Step2: The child component needs props to explicitly declare a prop. Subcomponent props can be written in two ways:
- Array form:
- Object form: Can be conditional
Child the parent
1. Callback parameter transmission
-
The parent passes the function to the child using prop, and when the child calls the function, it passes the data as an argument to the parent
Parent component:
<child2 :changeParent="changeMyself"></child2>Copy the code
methods: { // How the parent component changes its own data changeMyself (chidData) { this.parentChangeData = chidData } }Copy the code
In child components:
export default { // The child component displays functions that declare the props parent component props: { changeParent: { requried: true.// Indicates whether the message is mandatory type: Function // represents the data type } }, data () { return { childData: 'I'm going to pass this data to the parent.'}}}Copy the code
<button @click="changeParent(childData)">Click I pass the data to the parent component</button>Copy the code
2. Customize events + event listening
The child $emit defines a custom event with the event name as the first argument and the data as the second argument. The parent V-ON binds an event listener to get the child component’s data in the form of parameters in the method binding.
In child components:
methods: {
// Custom event $emit exposes data as an argument
changeParent () {
this.$emit('change'.this.childData)
}
}Copy the code
Parent component:
<! -- Parent component binding listener, get child component data -->
<child3 @change="changeMyself"></child3>Copy the code
methods: {
// How the parent component changes its own data
changeMyself (chidData) {
this.parentChangeData = chidData
}
}Copy the code
3. Direct access
The child is marked with ref, and the parent gets the child’s data directly through this.$refs[child ref].[Child attribute/method].
Note: “$refs is filled only after the component has rendered, and it is non-responsive. It is only intended as a contingency plan for direct access to child components — use of $refs in templates or computed properties should be avoided. The official website says so, just for emergency use, generally not recommended
Consider: single data flow
Prop is unidirectional: when a property of the parent component changes, it passes to the child component, but not vice versa. This is to prevent the child component from inadvertently modifying the state of the parent component to prevent the data flow of the application from becoming difficult to understand.
In addition, every time the parent component is updated, all prop of the child component is updated to the latest value. This means that you should not change prop inside child components. If you do, Vue will warn you on the console.
There are two situations where it is tempting to modify data in prop:
-
When Prop is passed in as an initial value, the child component wants to use it as local data.
-
Prop is passed in as raw data and processed by child components into other data output.
In both cases, the correct response is:
-
Define a local variable and initialize it with the value of prop:
-
Define a calculation property that handles the value of prop and returns:
Consider: bidirectional binding of child and parent
A single data flow prevents a child component from directly modifying the parent component’s data. In the real world, however, there are many times when you need to change the parent’s data in the child component.
Methods:
-
The parent has a method that changes its own data, passed to the child with prop, which is called when it needs to change the parent’s data. (Consistent with the callback parameter method)
-
The child component sends the data through an event, and the $emit event passes the data to the parent component, which listens and performs its own methods to change its data. (Consistent with the second method of passing from son to father)
-
The parent component has a method that changes its own data. The child calls the parent method directly through this.$parent
-
Parent component:
methods: { // How the parent component changes its own data changeMyself (chidData) { this.parentChangeData = chidData } }Copy the code
In child components:
export default { data () { return { childData: 'I am the data that the son passes to the father.'}},methods: { change () { // Through a direct access method call this.$parent.changeMyself(this.childData) } } }Copy the code
However, the above methods are event-based and cannot ensure that the data of the child and parent components are synchronized at all times
4. Use the.sync binding modifier to highlight bidirectional binding in the parent component prop. The child uses $emit(‘ update:data ‘,newVal) on watch to update the data sent by the parent prop. The parent does not need to listen for the update method.
Parent component:
<! Sync modifier --> <child6 :parentData.sync= "parentData"></child6>Copy the code
In child components:
props: ['parentData']Copy the code
// The child listens for data watch: { childData (newVal, oldVal) { this.$emit('update:parentData', newVal) } }Copy the code
-
The custom event here is a little different from the custom event passed from child to parent.
<child6 :parentData.sync="parentData"></child6>
Will be extended to:
<child6 :parentData"parentData" @update:parentData="val => parentData = val"></child6>
Think about:
The response expression to run when a custom event occurs is
In the “send data from child to parent via $emit event”, the response expression to run when a custom event occurs is: < child-@chang =”changeMyself”> changeMyself in .
In the former case, the expression val => bar = val means that the parent component’s data is forced to be equal to the data passed by the child, and in this case, we find that the parent and child components are equal. The parent can change the child (data) and the child can change the parent (data).
For the latter, changeMyself is defined in the parent component. In this function, you can do whatever you want with the data received from the child. The parent can change the child, but the child cannot change the parent directly! Changes to the data in the parent can only be determined by the parent itself.
There is a way to cut corners: If the props type is object or array, you can modify the props data in the child component without error detection. This makes data flow more difficult to analyze. In addition, if the props type is reference, Care should be taken to make deep copies of objects in child components to prevent implicit modification of objects in parent components.
Brother begets brother
1. Two sibling components have a common parent
Parent component:
<! The parent component passes the method of changing data to child2 and the data to Child, raising the data communication level to the parent component.
<child2 :changeParent="changeMyself"></child2>
<child :parent="parentChangeData"></child>Copy the code
Child2:
<! -- Child component calls method, taking data as argument -->
<button @click="changeParent(childData)">Click I pass the data to the parent component</button>Copy the code
export default {
// The child component displays functions that declare the props parent component
props: {
changeParent: {
requried: true.// Indicates whether the message is mandatory
type: Function // represents the data type
}
},
data () {
return {
childData: 'I'm going to pass this data to the parent.'}}}Copy the code
Child:
// Displays data for the props parent
props: {
parent: {
requried: true.// Indicates whether the message is mandatory
type: String.// represents the data type
default: 'I'm the default.'}}Copy the code
2. Route parameters are transmitted
Put the data that needs to be passed across the page after the URL, and get the URL string for the desired parameters when jumping to another page.
{
path: '/params/:id'
name: ' '.component: Sample
}Copy the code
<router-link :to="params/12">Hop routing</router-link>Copy the code
$route.params.id = 12, but this is only suitable for passing small data, such as numbers.
3.EventBus
First create a central time bus and introduce it into the components that need to be used. $emit(‘eventName’, value) with this.bus.$ON (‘eventName’, value => {this.print(value)}) Before $emit, you must already have $ON, so it is common to do $ON in a Created hook.
// Create a new bus.js file
import Vue from 'vue'
export default new Vue()Copy the code
Component A transmits data:
<script>
// Reference it in the component you want to use
import Bus from '@/components/Bus'
export default {
data () {
return {
childData: 'I'm brother A, put my data into eventBus'}},methods: {
submit () {
// Trigger the event
Bus.$emit('change'.this.childData)
}
}
}
</script>Copy the code
Component B receives data:
get () {
// Listen for receive events
Bus.$on('change', value => {
this.myData = value
})
}Copy the code
// Unbind the component when it is destroyed
destroyed () {
Bus.$off('change')}Copy the code
Communication between components can be achieved using EventBus, regardless of the number of components, as long as the eventName is different. It is clearer and more manageable to have an empty Bus instance as the central EventBus instead of directly accessing root.
Special eventBus
Traditional eventBus is only responsible for $EMIT and $ON and has no interaction with data. This makes the data not “persistent”, only valid after $emit, and the same component generates more than one $ON. When switching routes, the binding of the new component and the unbinding of the old component need to be considered.
$on = Bus; $on = Bus;
// Create a new bus.js file
// The Bus listens and puts the data directly into the Bus
import Vue from 'vue'
const Bus = new Vue({
data () {
return {
child7Val: ' '
}
},
created () {
this.$on('change', value => {
this.child7Val = value
})
}
})
export default BusCopy the code
The component that emits data does not change
<script>
// Reference it in the component you want to use
import Bus from '@/components/Bus'
export default {
data () {
return {
childData: 'I'm brother A, put my data into eventBus'}},methods: {
submit () {
// Trigger the event
Bus.$emit('change'.this.childData)
}
}
}
</script>Copy the code
The component that receives the data is modified to access the data directly from the Bus using computational properties, which are used to keep the data dynamic.
computed: {
child7Val () {
return Bus.child7Val
}
}Copy the code
Vuex
My understanding is that Vuex is a large repository dedicated to storing shared variables.
After Vuex is installed, create the store.js file
import Vue from 'vue'
import Vuex from 'vuex'
import app from './modules/app'
Vue.use(Vuex)
const store = new Vuex.Store({
state: {
// Store shared variables
msg: 'I'm raw data.'
},
getter: {
// Corresponds to the computed properties in store
mymsg: state= > {
return state.msg
}
},
mutations: {
// Change state,vue recommends using uppercase
MUTATIONSMSG (state, payload) {
state.msg = payload.msg
}
},
actions: {
// Similar to mutations, asynchronous is supported
mutationsMsg (context, payload) {
context.commit('MUTATIONSMSG', payload)
}
},
modules: {
app
},
strict: process.env.NODE_ENV ! = ='production'
})
export default storeCopy the code
When using mutations to modify state:
Component A uses data from the Store:
<template>
<div class="child">
<h3>Component A</h3>
{{$store.state.msg}}
</div>
</template>Copy the code
Component B modifies the data in store:
<script>
export default {
data () {
return {
myData: 'Data for Component B'}},methods: {
get () {
this.$store.commit('MUTATIONSMSG'.this.myData)
}
}
}
</script>Copy the code
When changing state using Actions:
<script>
export default {
data () {
return {
myData: 'Data for Component B'}},methods: {
get () {
this.$store.dispatch('mutationsMsg'.this.myData)
}
}
}
</script>Copy the code
State is used to store shared variables. This.$store.state.[variable] is used to obtain shared variables.
Getter, which can add a getter derived state (equivalent to a computed property in store), store.getters. The method name () is used to get the value of the shared variable.
Mutations are used to store the method for changing state.
Actions is also used to store methods for changing state, but action is based on mutations. Commit mutations method in Actions first, and dispatch by using the Actions component
thinking
-
The idea of componentization is to keep components independent and data independent
-
Modifying the value of component B directly through component A, such as bidirectional binding, is convenient but increases the coupling between components. The best thing to do is if component A changes the value of component B, expose the modified data, and let component B get the data and modify it.