preface
Components are one of the most powerful features of vue.js, and component instances are scoped independently 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 figure above, A and B, B and C, B and D are all father and son, C and D are brothers, and A and C are intergenerational (possibly multiple generations).
How to select effective communication modes according to different application scenarios? This is the subject of our discussion. This article summarizes several communication methods between vUE components, such as props, $emit/$ON, vuex, $parent /$children, $attrs/$Listeners and provide/inject, with easy to understand examples to explain the differences and use scenarios. I hope it will help my friends.
The code of this article please jab github blog, paper come zhongjue shallow, everyone more knock on the code!
Method a,props
/$emit
The parent component A gives props to the child component B, and the parent component B gives props to the child component B. The parent component B gives props to the child component B, and the parent component B gives props to the child component B, and the parent component B gives props to the child component B.
1. The parent component passes values to the child component
Vue :[“Henry”,”Bucky”,”Emily”] [“Henry”,”Bucky”,”Emily”]
// app.vue parent component <template> <div id=" App ">< users v-bind:users="users"></users> </template> <script> import Users from "./components/Users" export default {name: 'App', data(){ return{ users:["Henry","Bucky","Emily"] } }, components:{ "users":Users } }Copy the code
//users subcomponent <template> <div class="hello"> <ul> <li v-for="user in users">{{user}}</li> </template> <script> export default {name: 'HelloWorld', props:{users:{// this is the name of the parent tag type:Array, required:true}}} </script>Copy the code
Summary: The parent component uses props to pass data down to the child component. Note: There are three types of data in components: data, props, and computed
2. Child component passes value to parent component (by event form)
When we click the “vue.js Demo”, the child component passes the value to the parent component. The text changes from “passing a value” to “passing a value to the parent”, and the child component passes the value 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 to parent "); }}} </script>Copy the code
// Parent <template> <div id="app"> <app-header v-ON :titleChanged="updateTitle" ></app-header>// Consistent with child titleChanged custom event <h2>{{title}}</h2> </div> </template> <script> import Header from "./components/Header" Export default {name: 'App', data(){return{title:" pass a value "}}, methods:{updateTitle(e){this.title = e; } }, components:{ "app-header":Header, } } </script>Copy the code
Summary: When a child sends a message to its parent through Events, the child is essentially sending its own data to the parent.
Method 2,$emit
/$on
With an empty Vue instance serving as the central event bus (event center), which is used to trigger and listen for events, this approach achieves clever and lightweight communication between any component, including parent, sibling, and cross-level. The better state management solution, VUEX, was an option when our project was larger.
1. Specific implementation methods:
var Event=new Vue(); Event.$emit(Event name, data); $on(Event name,data => {});Copy the code
1. 2
Suppose there are three sibling components: A, B, and C. How does C obtain data from A or B
< div id = "itany" > < my - > a < / my - > a < my - b > b < / my - > < my - > c < / my - c > < / div > < template id = "a" > < div > < h3 > a component: {{name}}</h3> <button @click="send"> </button> </div> </template> <template id="b"> <div> <h3> < span style =" box-sizing: border-box; color: RGB (74, 74, 74); line-height: 20px; font-size: 13px! Important; white-space: inherit! Important;" {{name}}, {{age}} < / h3 > < / div > < / template > < script > var Event = new Vue (); 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: $on('data-a',name => {this.name = name; $on('data-b',age => {this.age = age; $on('data-b',age => {this. }) } } var vm = new Vue({ el: '#itany', components: { 'my-a': A, 'my-b': B, 'my-c': C } }); </script>Copy the code
$on
Custom events data-a and data-b are listened for. Because it is sometimes uncertain when the event will be fired, they are usually listened for in a Mounted or created hook.
Method three, VUEX
1. Briefly introduce the Vuex principle
Vuex implements a one-way data flow with a State globally to store data. When components want to change data in State, they must do so through Mutation, which also provides a subscriber mode for external plug-ins to call to get updates of State data. When all asynchronous operations (commonly seen in calling the back-end interface to asynchronously obtain update data) or batch synchronous operations need to take Action, but Action cannot directly modify State, the State data needs to be modified by Mutation. Finally, depending on the State change, render to the view.
2. Briefly introduce the functions of each module in the process:
- Vue Components: Indicates the Vue Components. HTML page, responsible for receiving user actions and other interactive behavior, execute the dispatch method to trigger the corresponding action for response.
- Dispatch: The operation behavior triggering method. It is the only method that can execute an action.
- Actions: Action behavior handling module, triggered by $store.dispatch(‘action name ‘, data1) in the component. Commit () then triggers the call to mutation, indirectly updating the state. Is responsible for handling all interactions received by Vue Components. Contains synchronous/asynchronous operations, supports multiple methods of the same name, and fires in the order in which they are registered. The operations requested to the background API are performed in this module, including triggering other actions and submitting mutations. This module provides encapsulation of promises to support chained triggering of actions.
- Commit: Indicates how to commit a state change. Submitting the mutation is the only way to execute the mutation.
- Mutations: Method of state change operation, triggered by COMMIT (‘mutation name ‘) in actions. Is the only recommended way for Vuex to modify state. This method can only be synchronized, and the method name must be globally unique. During the operation, some hooks will be exposed for state monitoring and so on.
- State: Page state management container object. The scattered data of data objects in Vue Components is stored in a centralized manner, which is globally unique for unified state management. The data required for the page display is read from this object, utilizing Vue’s fine-grained data response mechanism for efficient status updates.
- Getters: State object reading method. This module is not listed separately in the figure and should be included in render. Vue Components uses this method to read the global state object.
3. Vuex and localStorage
Vuex is the state manager of VUE, and the data stored is responsive. But it will not be saved, after the refresh is back to the initial state, the specific approach should be in VUEX data change when the data copy saved to localStorage inside, after the refresh, if there is data stored in localStorage, take out and replace the store state.
Let defaultCity = "Shanghai" try {// disable the local storage function, add a try... catch 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)); } catch (e) {}}}})Copy the code
In vuex, we save the state of the array, while localStorage only supports strings, so we need to use JSON conversion:
JSON.stringify(state.subscribeList); // array -> string
JSON.parse(window.localStorage.getItem("subscribeList")); // string -> array
Copy the code
Methods four,$attrs
/$listeners
1. Introduction
When multiple levels of component nesting need to pass data, the usual method is through VUEX. But if you just pass the data, without intermediate processing, using VUEX processing is a bit of overkill. For this purpose, vvue2.4 provides an alternative method —-$attrs/$Listeners
-
$attrs: contains feature bindings (except class and style) that are not identified (and obtained) in the parent scope of a prop. When a component does not declare any prop, all parent bindings (except class and style) are included, and internal components can be passed through v-bind=”$attrs”. Usually used in conjunction with the inheritAttrs option.
-
$Listeners: Contains v-ON event listeners in the parent scope (without.native decorators) It can pass in internal components via V-on =”$Listeners”
Let’s look at an example of cross-level communication:
// index.vue <template> <div> <h2> < p style =" text-align: center; text-align: center;" ></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>Copy the code
// childcom1. vue <template class="border"> <div> <p>foo: {{foo}}</p> {{ $attrs }}</p> <child-com2 v-bind="$attrs"></child-com2> </div> </template> <script> const childCom2 = () => import("./childCom2.vue"); Export default {components: {childCom2}, function () {function_props: {function_props: {function_props: {function_props: {function_props: {function_props: {function_props: {function_props: {function_props: String // foo as props}, created() {console.log(this.$attrs); / / {" boo ", "Html", "coo" : "CSS", "scooby-doo" : "Vue", "title" : "front-end craftsman"}}}; </script>Copy the code
// 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 ", "scooby-doo" : "Vue", "title" : "front-end craftsman"}}}; </script>Copy the code
// childCom3.vue
<template>
<div class="border">
<p>childCom3: {{ $attrs }}</p>
</div>
</template>
<script>
export default {
props: {
coo: String,
title: String
}
};
</script>
Copy the code
This is shown in the figure above$attrs
Represents an object with no inherited data. The format is {property name: property value}. Vue2.4 provides$attrs
, $listeners
To pass data and events, making communication between components across levels easier.
$listeners = Props; $listeners = Props; $listeners = Props; $listeners = Props; $listeners = Props; $listeners = Props;
Provide /inject
1. Introduction
The new API in Vue2.2.0 requires this pair of options to be used together to allow an ancestor component to inject a dependency into all of its descendants, regardless of the component level, for as long as the upstream and downstream relationships are established. In a nutshell: the ancestor component supplies variables via a provider, and the descendant component injects variables via a inject. The Provide/Inject API mainly solves the communication problem between cross-level components, but its use scenario is that the child component obtains the state of the parent component, and the cross-level components establish a kind of active provision and dependency injection relationship.
1. 2
Suppose there are two components: A. value and B. value, and B is A child of A
// a.vue export default {provide: {name: 'provide'}}Copy the code
// B.vue export default { inject: ['name'], mounted () { console.log(this.name); // Make a boat in the sea}}Copy the code
As you can see, in a.vue, we have the provide: name variable with the value of “provide: name”. Its function is to provide the name variable to all of its children. If b. ue injects the name variable provided by component A, then component B can access this. Name directly, and its value will be the same as that of component B. This is the core use of the provide/Inject API.
Note that provide and inject bindings are not responsive. This is deliberate. However, if you pass in an object that can be listened to, then the object properties are still responsive —-vue official documentation. So, if the name of a. vue is changed above, the this.name of B.vue is not changed.
3. Provide and Inject How to implement data response
In general, there are two ways:
- Provide an instance of the ancestor component, and then inject dependencies into the descendant component. This allows you to modify the properties of the instance of the ancestor component directly in the descendant component. The downside of this approach is that many unnecessary things such as props and methods are mounted on the instance
- Optimize responsive provide with the latest 2.6 API vue.Observable (recommended)
Let’s take an example: components D, E, and F get the color value passed by component A and implement A data-responsive change, that is, components D, E, and F change with the color of component A (core code:)
/ / A component
<div>
<h1>A component</h1>
<button @click="() => changeColor()">Change the color</button>
<ChildrenB />
<ChildrenC />
</div>
......
data() {
return {
color: "blue"
};
},
// provide() {
// return {
// theme: {
// The data bound in this way is not responsive
/ /} / / that is A component of the color change, component D, E, F don't follow
/ /};
// },
provide() {
return {
theme: this// Method 1: provide an instance of the ancestor component
};
},
methods: {
changeColor(color) {
if (color) {
this.color = color;
} else {
this.color = this.color === "blue" ? "red" : "blue"; }}}// Approach 2: Optimize responsive provide using the latest 2.6 API vue. Observable
// 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";
/ /}
/ /}
// }
Copy the code
<div class="border2"> <h3 :style="{color: RGB (74, 74, 74); line-height: 20px; white-space: normal;" Injections. Theme. Color} "> F components < / h3 > < / div > < / template > < script > export default {inject: {the theme: Default: () => ({})}}; </script>Copy the code
Provide and Inject are mainly used to provide use cases for higher-level plug-in/component libraries, but if you are skilled in using them in your business, you can get twice the result with half the effort!
Methods six,$parent
/ $children
withref
ref
: If used on a normal DOM element, the reference refers to the DOM element. If used on a child component, the reference points to the component instance$parent
/$children
: Access the parent/child instance
It’s important to note that both get the component instance directly and use it to call the component’s methods or access data directly. Let’s look at an example of using a ref to access a component:
Export default {data () {return {title: 'vue.js'}}, methods: { sayHello () { window.alert('Hello'); }}}Copy the code
// parent <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(); // Popover}} </script>Copy the code
However, the disadvantage of these two methods is that they cannot communicate across levels or between brothers.
// parent.vue
<component-a></component-a>
<component-b></component-b>
<component-b></component-b>
Copy the code
In this case, we need to configure additional plug-ins or tools, such as the Vuex and Bus solutions.
conclusion
Common usage scenarios can be divided into three categories:
- Father-son communication:
The parent passes data to the child via props, and the child to the parent via 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
I recommend a good BUG monitoring toolFundebug, welcome to the free trial!
Refer to the article
- Everest Architecture (highly recommended)
- Vue. Js component elaborate
- Vue.js official document
- Vue development combat
- Vuex data is stored locally
- Vuex framework principle and source code analysis
- Detailed description of Vue component communication modes