Preface
Component is one of the most powerful functions of vue.js, and the scope of component instances is independent of each other, which means that data between different components cannot be referenced. In general, components can have the following relationships:
As shown in the above figure, A and B, B and C, B and D are father-son relationships, C and D are brotherly relationships, and A and C are intergenerational relationships (possibly across multiple generations).
According to different usage scenarios, how to choose effective communication methods? This is the subject we are going to discuss. This paper summarizes several communication methods between vue components, such as props,$emit
/$on
、vuex、$parent
/$children
、$attrs
/$listeners
And I hope it will be of some help to my small partners.
Please stamp the code of this articleGithub blog, the paper finally feel shallow, everyone began to knock more code!
Method 1,props
/$emit
Parent component a is passed to child component b through props, and B to A is implemented through $ emit in component b and v-on in component a.
1. parent component transmits value to child component
Next, we will illustrate how the parent component passes the value to the child component through an example: how to obtain the data in the parent component App.vue in the child component Users.vueusers:["Henry","Bucky","Emily"]
//App.vue parent component
<template>
<div id="app">
< users v-bind: users = "users" > </users >//the former custom name is convenient for subcomponents to call, while the latter passes the data name
</div>
</template>
<script>
import Users from "./components/Users"
export default {
name: 'App',
data(){
return{
users:["Henry","Bucky","Emily"]
}
},
components:{
"users":Users
}
}
//users subcomponent
<template>
<div class="hello">
<ul>
< Li v-for = "userin users" > {{user}} </Li >//iterate through the passed values and render to the page
</ul>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
props:{
Users:{ // This is the custom name of the child tag in the parent component
type:Array,
required:true
}
}
}
</script>
Summary: The parent component passes data down through props to the child component. Note: There are three forms of data in the component: Data, props, computed
2. The child component transmits the value to the parent component (in the form of an event)
Next, we will illustrate how a child component transfers a value to a parent component through an example: when we click “Vue.js Demo”, the child component transfers the value to the parent component, and the text changes from “transferring a value” to “transferring a value from a child to the parent component”, thus realizing the transfer of the value from the child component to the parent component.
//subcomponent
<template>
<header>
< h1 @ click = "changetitle" > {{title}} </h1 >//bind a click event
</header>
</template>
<script>
export default {
name: 'app-header',
data() {
return {
title:"Vue.js Demo"
}
},
methods:{
changeTitle() {
This.$emit("titleChanged ","child passes value to parent component "); //Custom Event Pass Value "Child Pass Value to Parent Component"
}
}
}
</script>
//parent component
<template>
<div id="app">
< app-header v-on: titleChanged = "updatetitle" > </app-header >//consistent with sub-component titlechanged custom event
// updateTitle($event) accepts the passed text
<h2>{{title}}</h2>
</div>
</template>
<script>
import Header from "./components/Header"
export default {
name: 'App',
data(){
return{
Title: "A value was passed."
}
},
methods:{
UpdateTitle(e){ // declares this function
this.title = e;
}
},
components:{
"app-header":Header,
}
}
</script>
Summary: When a child component sends a message to a parent component through events, it is actually the child component that sends its own data to the parent component.
Method 2,$emit
/$on
This method uses an empty Vue instance as the central event bus (event center) to trigger events and monitor events. It skillfully and lightly realizes communication between any component, including father and son, brother and cross-level.. When our project is relatively large, we can choose a better state management solution vuex.
1. Specific implementation mode:
var Event=new Vue();
Event.$emit (event name, data);
Event.$on (event name, data => {});
2. For example
Assuming that there are three sibling components, namely, component a, component b and component c, how can component c obtain the data of component a or component b
<div id="itany">
<my-a></my-a>
<my-b></my-b>
<my-c></my-c>
</div>
<template id="a">
<div>
< h3>A component: {{name}}</h3 >
< button @click="send "> send data to component c < /button >
</div>
</template>
<template id="b">
<div>
< H3 > component b: {{age}}</h3 >
< button @click="send "> send array to c component < /button >
</div>
</template>
<template id="c">
<div>
< h3>C component: {{name}}, {{age}}</h3 >
</div>
</template>
<script>
var Event = new Vue(); //define an empty Vue instance
var A = {
template: '#a',
data() {
return {
name: 'tom'
}
},
methods: {
send() {
Event.$emit('data-a', this.name);
}
}
}
var B = {
template: '#b',
data() {
return {
age: 20
}
},
methods: {
send() {
Event.$emit('data-b', this.age);
}
}
}
var C = {
template: '#c',
data() {
return {
name: '',
age: ""
}
},
Mounted() {// is executed after the template compilation is completed
Event.$on('data-a',name => {
this.name = name; //no new this will be generated inside the arrow function. if = >isnot used here, this refers to Event
})
Event.$on('data-b',age => {
this.age = age;
})
}
}
var vm = new Vue({
el: '#itany',
components: {
'my-a': A,
'my-b': B,
'my-c': C
}
});
</script>
$on
Custom events data-a and data-b are monitored, because sometimes it is uncertain when the event will be triggered, and it is usually monitored in the mounted or created hook.
Method 3, vuex
1. Briefly introduce Vuex principle
Vuex implements a one-way data flow and has a State to store data in the whole world. when a component wants to change the data in the State, it must do so through Mutation. Mutation also provides subscriber mode for external plug-ins to call to obtain updates of State data. However, when all asynchronous operations (usually calling back-end interface to obtain updated data asynchronously) or batch synchronous operations need to go through Action, but Action cannot directly modify State, or the data of State needs to be modified through Mutation. Finally, according to the change of State, render it to the view.
2. Briefly introduce the functions of each module in the process:
- Vue Components:Vue components. On the HTML page, it is responsible for receiving interactive behaviors such as user operations and executing the dispatch method to trigger the corresponding action to respond.
- Dispatch: The trigger method of operation behavior is the only method capable of executing action.
- actions:The operation behavior processing module consists of
Dispatch ('action name', data1)
To trigger. Then commit () triggers the invocation of mutation and indirectly updates state. Responsible for handling all interactions received by vueccomponents. Contains synchronous/asynchronous operations, supports multiple methods with the same name, and triggers in sequence according to the registration order. Operations requested from the background API are carried out in this module, including triggering other action and submitting mutation. This module provides the package of Promise to support chain triggering of action. - Commit: state change commit operation method. Submitting mutation is the only way to execute mutation.
- mutations:State change operation method, by actions
Commit('mutation name')
To trigger. It is the only recommended method for Vuex to modify state. This method can only be synchronized, and the method name can only be globally unique. During the operation, some hook will be exposed for state monitoring, etc. - State: Page State Management Container Object. Centralized storage of scattered data of data objects in Vue components, globally unique, for unified state management. The data required for page display is read from the object, and Vue’s fine-grained data response mechanism is used for efficient state update.
- Getters:state object reading method. The module is not listed separately in the figure and should be included in the render. Vue Components reads the global state object through this method.
3.Vuex and localStorage
Vuex is vue’s status manager, and the stored data is responsive. But it will not be saved and will return to its original state after refreshing.The specific method should be to save a copy of the data in localStorage when the data in vuex changes. after refreshing, if there is saved data in localStorage, take it out and replace the state in store.
Let defaultCity = "shanghai"
Try {// The user turned off the local storage function and added a try...catch to the outer layer at this time
if (! defaultCity){
defaultCity = JSON.parse(window.localStorage.getItem('defaultCity'))
}
}catch(e){}
export default new Vuex.Store({
state: {
city: defaultCity
},
mutations: {
changeCity(state, city) {
state.city = city
try {
window.localStorage.setItem('defaultCity', JSON.stringify(state.city));
//when the data changes, copy the data and save it in localStorage.
} catch (e) {}
}
}
})
It should be noted here that since in vuex, all the states we save are arrays, while localStorage only supports strings, JSON conversion is required:
JSON.stringify(state.subscribeList); // array -> string
JSON.parse(window.localStorage.getItem("subscribeList")); // string -> array
Method 4,$attrs
/$listeners
1. Introduction
When multi-level component nesting needs to transfer data, the commonly used method is vuex. However, it would be a bit overkill to use vuex processing if only the data is transferred without intermediate processing. This VUE version 2.4 provides another method–.$attrs
/$listeners
-
$attrs
: Contains attribute bindings (except class and style) in the parent scope that are not recognized (and obtained) by prop. When a component does not declare any prop, all parent scope bindings (except class and style) are included here and can be passed into internal components through v-bind=”$attrs “. It is usually used in conjunction with interitAttrs option. -
$listeners
: Contains the v-on event listener in the parent scope (without the. native modifier). It can be passed into internal components via v-on=”$listeners “
Next, let’s look at an example of cross-level communication:
// index.vue
<template>
<div>
< h2 > sailing in the waves < /h2 >
<child-com1
:foo="foo"
:boo="boo"
:coo="coo"
:doo="doo"
Title= "front-end craftsman"
></child-com1>
</div>
</template>
<script>
const childCom1 = () => import("./childCom1.vue");
export default {
components: { childCom1 },
data() {
return {
foo: "Javascript",
boo: "Html",
coo: "CSS",
doo: "Vue"
};
}
};
</script>
// childCom1.vue
<template class="border">
<div>
<p>foo: {{ foo }}</p>
< p>childCom1 $attrs: {{ $attrs }}</p >
<child-com2 v-bind="$attrs"></child-com2>
</div>
</template>
<script>
const childCom2 = () => import("./childCom2.vue");
export default {
components: {
childCom2
},
InheritAttrs: false, // can turn off attributes that are automatically mounted on the component root element and are not declared in props
props: {
Foo: String // foo bound as props property
},
created() {
console.log(this.$attrs); //{"boo": "html", "coo": "CSS", "doo": "vue", "title": "front-end craftsman"}
}
};
</script>
// childCom2.vue
<template>
<div class="border">
<p>boo: {{ boo }}</p>
<p>childCom2: {{ $attrs }}</p>
<child-com3 v-bind="$attrs"></child-com3>
</div>
</template>
<script>
const childCom3 = () => import("./childCom3.vue");
export default {
components: {
childCom3
},
inheritAttrs: false,
props: {
boo: String
},
created() {
console.log(this.$attrs); //{"COO": "CSS", "DOO": "Vue", "Title": "Front End Craftsman"}
}
};
</script>
// childCom3.vue
<template>
<div class="border">
<p>childCom3: {{ $attrs }}</p>
</div>
</template>
<script>
export default {
props: {
coo: String,
title: String
}
};
</script>
As shown in the figure above$attrs
Represents an object that does not inherit data and has the format {Attribute Name: Attribute Value}. Vue2.4 provides$attrs
,$listeners
To transfer data and events, communication between cross-level components becomes easier.
In short:$attrs
And$listeners
It’s two objects,$attrs
Is stored in the parent component binding non-Props properties,$listeners
The store is a non-native event bound in the parent component.
Method v. provision/injection
1. Introduction
Vue2.2.0 adds API, which needs to be used together.To allow an ancestor component to inject a dependency into all its descendants, no matter how deep the component level is, and it will always take effect when the upstream-downstream relationship is established.. In a nutshell: variables are provided by provider in the ancestor component and then injected by inject in the descendant component.
The provide/inject API mainly solves the communication problem between cross-level components. However, its usage scenario is that sub-components obtain the state of higher-level components. A relationship between active provision and dependency inject ion is established between cross-level components..
2. For example
Suppose there are two components: A.vue and b.vue. b is a subcomponent of a.
// A.vue
export default {
provide: {
Name:' Sailing in the Waves'
}
}
// B.vue
export default {
inject: ['name'],
mounted () {
console.log(this.name); //Sailing in the Waves
}
}
As you can see, in A.vue, we set up aprovide: name, value for the wave in the boat, its role is tonameThis variable is provided to all its subcomponents. And in B.vue, throughinject
Injected with thenameVariable, then in component b, it can be passed directly throughthis.nameAfter accessing this variable, its value is the same as sailing in the waves. This is the core usage of the provider/injectapi.
It should be noted that:Provide and inject bindings are not responsive. This is deliberately done. However, if you pass in a monitorable object, its object’s properties are still responsive—-vue Official Documents
Therefore, if the name of A.vue above changes, the name of B.vue’s this.name will not change, and it will still be sailing on the waves.
3. How do 3.provide and inject implement data response
In general, there are two ways:
- Provide an instance of an ancestor component, and then inject dependencies into the descendant component, so that the attributes of the instance of the ancestor component can be directly modified in the descendant component. However, this method has the disadvantage that many unnecessary things such as props and methods are mounted on this instance.
- Use the latest API Vue.observable in 2.6 to optimize the responsive provide (recommended)
Let’s look at an example: sun components d, e and f obtain the color value passed by component a and can realize data responsive change, i.e. components d, e and f will not change after the color of component a changes (core code is as follows:)
// A component
<div>
< h1>A component < /h1 >
Color</button @click="() => changeColor () "> changecolor </button >
<ChildrenB />
<ChildrenC />
</div>
......
data() {
return {
color: "blue"
};
},
// provide() {
// return {
// theme: {
// color: this.color // data bound in this way is not responsive
//}//i.e. after the color of component a changes, components d, e and f will not change accordingly.
// };
// },
provide() {
return {
Theme: this///method one: provide an instance of the ancestor component
};
},
methods: {
changeColor(color) {
if (color) {
this.color = color;
} else {
this.color = this.color === "blue" ? "red" : "blue";
}
}
}
//Method 2: Use the latest API Vue.observable in 2.6 to optimize the responsive provide
// provide() {
// this.theme = Vue.observable({
// color: "blue"
// });
// return {
// theme: this.theme
// };
// },
// methods: {
// changeColor(color) {
// if (color) {
// this.theme.color = color;
// } else {
// this.theme.color = this.theme.color === "blue" ? "red" : "blue";
// }
// }
// }
// F component
<template functional>
<div class="border2">
< H3: style = "{color: injections.theme.color}" > f component < /h3 >
</div>
</template>
<script>
export default {
inject: {
theme: {
//Functional components have different values
default: () => ({})
}
}
};
</script>
Although provide and inject mainly provide use cases for high-level plug-ins/component libraries, if you can use them skillfully in your business, you can get twice the result with half the effort!
Method 6,$parent
/$children
Andref
-
ref
: If used on ordinary DOM elements, the reference refers to DOM elements; If used on a subcomponent, the reference points to the component instance -
$parent
/$children
: Access parent/child instances
It should be noted that both of them directly obtain component instances and can directly call component methods or access data after use. Let’s take a look firstref
To access an example of a component:
// component-a subcomponent
export default {
data () {
return {
title: 'Vue.js'
}
},
methods: {
sayHello () {
window.alert('Hello');
}
}
}
//parent component
<template>
<component-a ref="comA"></component-a>
</template>
<script>
export default {
mounted () {
const comA = this.$refs.comA;
console.log(comA.title); // Vue.js
comA.sayHello(); //popup windows
}
}
</script>
However,The disadvantage of these two methods is that they cannot communicate between different levels or brothers..
// parent.vue
<component-a></component-a>
<component-b></component-b>
<component-b></component-b>
We want to access the two component-b components in the page that references it (here is parent.vue) in Component-A. In this case, we have to configure additional plug-ins or tools, such as Vuex and Bus solutions.
Summary
Common usage scenarios can be divided into three categories:
- Parent-child communication:
The parent passes data to the child through props, and the child passes data to the parent through events ($emit
); Communication is also possible through parent/child chains ($parent
/$children
); Ref can also access component instances; provide / inject API;$attrs/$listeners
- Brother correspondence:
Bus; Vuex
- Cross-level communication:
Bus; Vuex; provide / inject API、$attrs/$listeners
To recommend a useful BUG monitoring toolFundebug, welcome to try free!
Welcome to pay attention to the public number:Front end craftsmanWe witness your growth together!