Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to communicate between vue components

2025-01-16 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/02 Report--

This article introduces the knowledge of "how to communicate between vue components". In the operation of actual cases, many people will encounter such a dilemma, so let the editor lead you to learn how to deal with these situations. I hope you can read it carefully and be able to achieve something!

Vue is a framework for data-driven view update, we usually develop, we will split the different modules of the page into a vue component, so the data communication between components is very important for vue, so how to carry out data communication between components?

First of all, we need to know what relationships exist between components in vue to make it easier to understand how they communicate.

Generally speaking, we can be divided into the following relationships:

Communication between parent and child components

Communication between non-parent-child components (sibling components, intergenerational relationship components, etc.)

Props / $emit

The parent component passes data to the child component through props, while the $emit child component can communicate with the parent component.

The parent component passes values to the child component

Import child from'. / child.vue'export default {name: 'HelloWorld', components: {comArticle}, data () {return {msg:' A Li Wang'} {{msg}} export default {props: {msg: String}}

Note:

Prop can only be passed from the upper-level component to the next-level component (parent-child component), which is called unidirectional data flow. And prop is read-only and cannot be modified. All changes will be invalidated and warned.

First, the prop should not be changed within a subcomponent, which can break one-way data binding and make the data flow difficult to understand. If you need to do so, you can receive it through the data property or use the computed property for transformation.

Second, if props passes a reference type (object or array), change the object or array in the child component, the state of the parent component will also be updated accordingly, and take advantage of this to achieve "two-way binding" of parent and child component data, although this implementation can save code, but at the expense of data flow simplicity, difficult to understand, it is best not to do so.

To achieve "two-way binding" of data for parent and child components, you can use v-model or .sync.

The child component passes the value to the parent component

Use $emit to send data to the parent component, the principle is like this: the parent component listens to the function through v-on and receives parameters in the child component, and the vue framework listens to the fn event function of your vMuon = "fn" in the child component, and it can be triggered in the child component using $emit, so write an example below.

Import child from'. / child.vue'export default {name: 'HelloWorld', components: {comArticle}, data () {return {msg:' A Li Wang'}}, methods: {changMsg (msg) {this.msg = msg} {{msg}} change the string export default {props: {msg: String} Methods: {change () {this.$emit ('changMsg',' A Li Wang takes you to learn the front end')}} v-model instruction

V-model is used to create two-way bindings on form controls or components. It is essentially a syntax sugar for v-bind and v-on. Using v-model on a component, a prop named value and an event named input are bound to the component by default.

When one of the prop in our component needs to implement the above-mentioned "two-way binding", v-model can do its best. With it, there is no need to manually bind and listen for custom events on the current instance on the component, which makes the code more concise.

The following is a core code implemented by an input component to introduce the application of v-model.

Export default {data () {return {input:''}},} export default {data () {return {currentValue: this.value = undefined | | this.value = null?'}}, props: {value: [String Number], / / key 1}, methods: {handleInput (event) {const value = event.target.value This.$emit ('input', value); / / key 2},},}

As can be seen in the above example, the essence of the v-bind model = "inputValue" is the syntax sugar of v-bind and v-on. By default, the parent component is bound with an attribute named value= "inputValue" and the @ input= "(v) = > {this.inputValue = v}" event. The child component notifies the parent component through this.$emit ('input', value).

Therefore, his principle is also to use the parent-child component parameter props / $emit method we mentioned above to achieve two-way binding.

Sometimes a property named value has a special meaning in certain controls, and this conflict can be avoided with the v-model option.

.sync modifier

The .sync modifier is available in vue 1.x, where when a child component changes the value of a prop with .sync, it is synchronized to the value in the parent component. This is very convenient to use, but the problem is also very obvious, which destroys the one-way data flow. When the application is complex, the cost of debug will be very high.

As a result, .sync is removed from vue 2.0. But in practical applications,. Sync has its own application scenarios, so in vue version 2.3, it ushered in a brand new .sync.

The new .sync modifier is no longer a true two-way binding, it is similar in nature to v-model, just an acronym.

Examples of normal encapsulated components:

The above code can be written using .sync

In this way, in the subcomponent, you can reassign the prop with the following code.

This.$emit ('update:title', newTitle)

When you see here, do you find that the .sync modifier is very similar to v-model, which is also syntactic sugar, and v-bind:title.sync is equivalent to "doc.title", which is equivalent to "doc.title = $event".

Comparison between v-model and .sync

Sync is very similar to v-model in terms of function, which is to achieve "two-way binding" of data. In essence, sync is not really two-way binding, but syntax sugar.

By comparison,. Sync is more flexible, it can be used by multiple prop, while v-model can only have one in a component.

Semantically, the value of v-model binding refers to the bound values of this component, such as input component, select component, date and time selection component, color picker component, the values bound by these components are more appropriate to use v-model. In other cases, without such semantics, I personally think it is better to use .sync.

Parent/parent / parent/children

You can access the instance of the component through $parent and $children. What does it mean to get the instance? Represents all methods and data that have access to this component. Liezi is as follows:

{{msg}} Click to change the subcomponent value import ComA from'. / test/comA.vue'export default {name: 'HelloWorld', components: {ComA}, data () {return {msg:' Welcome'}}, methods: {changeA () {/ / get the subcomponent A this.$children [0] .messageA = 'this is new value'} {{messageA}}

Get the value of the parent component as: {{parentVal}}

Export default {data () {return {messageA: 'this is old'}}, computed: {parentVal () {return this.$parent.msg;}

Pay attention to the boundary case, for example, if you get an instance of new Vue () with $parent on # app, you get undefined with $parent on this instance, and $children is an empty array in the lowest subcomponent. Also notice that the values of parent and parent are different from those of parent and children. The value of $children is an array, while the value of $parent is an object.

Props $emit and $parent $children are used for communication between parent and child components, while using props for parent-child communication is more common, and neither of them can be used for communication between non-parent and child components.

Provide / inject

Provide / inject is a new api added by vue2.2.0, which simply means that variables are provided through provide in the parent component, and then injected into the child component through inject.

Official description: this pair of options need to be used together to allow an ancestral component to inject a dependency into all its descendants, no matter how deep the component level is, and will always take effect for the time when its upstream and downstream relationship is established.

The provide option should be

An object or a function that returns an object. This object contains properties that can be injected into its descendants. You can use ES2015 Symbols as key in this object, but it only works in environments that natively support Symbol and Reflect.ownKeys.

The inject option should be:

An array of strings

An object (click here for details)

Basic usage / / ancestor components provide foo// first export default {name: "father", provide () {return {foo:'hello'}}, / / second export default {name: "father", provide: {foo:'hello~~~~'},} / / descendant components are injected into foo, directly as this.foo to use export default {inject: ['foo'],}

Is there any difference between the above two uses?

If you just pass a string, like the hello above, it makes no difference and can be read by future generations.

If you need the value of the this object property (as shown in the code below), the second cannot be passed, and the descendant components cannot get the data. So it is recommended to write only the first one.

/ / when you pass an object to future generations, provide () {return {test: this.msg}}

Note: once some data is injected, such as foo in the example above, the data foo can no longer be declared in this component because it is already occupied by the parent.

Provide and inject bindings are not responsive.

It was done on purpose. However, if you pass in a listenable object, the properties of the object are responsive. Because the object is a reference type.

Let's start with an example of data of value type (that is, string), which will not respond.

Provide () {return {test:this.msg}}, data () {return {msg: "Welcome to Your Vue.js App",}} mounted () {setTimeout () = > {this.msg = "halo world"; console.log (this._provided.msg) / / log:Welcome to Your Vue.js App}, 3000)}

As shown above, this will not work, the data in the printed _ provided has not changed, and the value obtained by the subcomponent has not changed.

You can even assign a value to this._provided.msg directly, but even if the value in _ provided.msg changes, the value of the subcomponent remains the same.

When your parameter is an object, you can respond as follows:

Provide () {return {test:this.activeData}}, data () {return {activeData: {name:'halo'},}} mounted () {setTimeout (() = > {this.activeData.name = 'world';}, 3000)}

This is what the vue official wrote that the properties of the object are responsive.

Provide/inject implements global variables

Isn't provide/inject passed on only from ancestors to future generations? Yes, but if we bind to the topmost component, app.vue, whether all descendants receive it, it will be used as a global variable.

/ / app.vueexport default {name: 'App', provide () {return {app:this}}, data () {return {text: "it's hard to tell the night time from the day"}}, methods: {say () {console.log ("Desperado, why don't you come to your senses?")}} / / all other sub-components that require global variables Only need to inject app on demand to export default {inject: ['foo','app'], mounted () {console.log (this.app.text) / / get the variable this.app.say () in app; / / you can execute the method in app and turn it into a global method! }} provide/inject enables page refresh without flickering

Reroute to the current page with vue-router, the page is not refreshed

When refreshed with window.reload () or router.go (0), the whole browser is reloaded, flashes, and the experience is not good.

So what do we do?

Similar to the above principle, we only write a function in the component that controls routing (use v-if to control the display and hiding of router-view, the principle here is not detailed), then pass this function to future generations, and then call this method in future generations to refresh the route.

/ / app.vueexport default {name: 'App', provide () {return {reload: this.reload}}, data () {return {isShowRouter: true,}}, methods: {reload () {this.isShowRouter = false; this.$nextTick (() = > {this.isShowRouter = true })} / / descendant components export default {inject: ['reload'],}

Here provide uses a function to be passed to future generations, and then future generations call this function, which is also a way to communicate parameters to the parent component as descendants. The principle here is very similar to event event subscription publication.

Ref / $refs

Ref: if used on ordinary DOM elements, references point to DOM elements; if used on sub-components, references point to component instances, through which you can directly call component methods or access data. Let's look at an example of ref to access components:

/ / Child component A.vueexport default {data () {return {name: 'Vue.js'}}, methods: {sayHello () {console.log (' hello')} / / parent component app.vue export default {mounted () {const comA = this.$refs.comA; console.log (comA.name); / / Vue.js comA.sayHello () / / hello}}

Ref this way, is to obtain the instance of the child component, and then can directly access the method of the child component and the data of the operation data. It is a way for the parent component to control the child component. If the child component wants to pass parameters or operations to the parent component, it can only pass parameters or operations to the parent component in other ways.

EventBus

EventBus, in fact, the principle is event subscription and publication, eventBus, also known as event bus, in vue can be used as a bridge concept, just as all components share the same event center, you can register with the center to send or receive events, so components can notify other components.

Here, we can directly use the event listening that comes with vue, that is, emitemitemiton. Let's simply encapsulate it:

First, you need to create an event bus and export it so that other modules can use or listen to it.

Create a new event-bus.js file

/ / event-bus.jsimport Vue from 'vue'export const EventBus = new Vue ()

Occurrence of events

Suppose you have two components: additionNum and showNum, which can be sibling components or parent-child components; here we take sibling components as an example:

Import showNumCom from'. / showNum.vue'import additionNumCom from'. / additionNum.vue'export default {components: {showNumCom, additionNumCom}} / / addtionNum.vue send event + adder import {EventBus} from'. / event-bus.js'console.log (EventBus) export default {data () {return {num: 1}}, methods: {additionHandle () {EventBus.$emit ('addition') {num: this.num++})}

Receive event

/ / receive event calculation in showNum.vue and: {{count}} import {EventBus} from'. / event-bus.js'export default {data () {return {count: 0}}, mounted () {EventBus.$on ('addition', param = > {this.count = this.count + param.num;})}}

This makes it possible to click the add button in the component addtionNum.vue and display the summation result in showNum.vue using the passed num.

Remove event listeners

If you want to remove the listening for an event, you can do something like this:

Import {eventBus} from 'event-bus.js'EventBus.$off (' addition') encapsulates a set of eventBus by itself

Here, you can use your own package of eventBus to make it easier for you to do whatever you want. Here is a sealed set for everyone.

/ * eslint-disable no-console * / / event mapping table let eventMap = {} / * * listening event * @ param {string} eventName event name * @ param {function} listener callback function * @ param {object} instance registered event instance * / function on (eventName, listener, instance) {eventMap [eventName] = eventMap [eventName] | [eventMapName] .push ({listener, instance) })} / / listen for events Execute function once (eventName, listener, instance) {eventMap [eventName] = eventMap [eventName] | | [] eventMap.push ({listener, instance, once: true,})} / / unlisten for events function off (eventName) Listener) {/ / unlisten for all events if (! eventName) {eventMap = {} return} / / there is no corresponding event if (! eventMap [eventName]) {return} / / release an event to listen for an event [eventName] .forEach ((currentEvent, index) = > {if (currentEvent.listener = listener) {eventMap.splice (index, 1)})} / / send an event Execute the corresponding response function function emit (eventName,... args) {if (! eventMap [eventName]) {return} eventMaps [eventName] .forEach ((currentEvent, index) = > {currentEvent.listener (... args) if (currentEvent.once) {eventMap.splice (index, 1)})} / / displays the currently registered events Code optimization uses function showEventMap (targetEventName) {if (targetEventName) {/ / View the listening of a specific event eventMap.forEach (eventItem = > {console.log (targetEventName, eventItem.instance, eventItem.listener)})} else {/ / View the listening of all events Object.keys (eventMap) .forEach (eventName = > {eventMapEventName] .forEach (eventItem = > {console.log (eventName, eventItem.instance)) EventItem.listener)}} / / provide vue mixin method When beforeDestroy automatically logoff event listeners export const mixin = {created () {/ / overload on function to collect events that this component listens to, and when to eliminate, destroy event listeners this.$eventListenerList = [] this.$event = {off, once, emit, showEventMap} this.$event.on = (eventName, listener) = > {this.$eventListenerList.push ({eventName, listener}) on (eventName, listener)}}, / / eliminate components Auto destroy event listener beforeDestroy () {this.$eventListenerList.forEach (currentEvent = > {off (currentEvent.eventName, currentEvent.listener)})},} export default {on, off, once, emit, showEventMap}

How to use it, just introduce it in the main.js of the project, and then Vue.mixin, as follows:

/ / main.jsimport Vue from 'vue'import {mixin as eventMixin} from' @ / event/index'Vue.mixin (eventMixin)

In other files of the vue project, you can directly this.$event.on this.$event.$emit as follows:

This.$event.on ('test', (v) = > {console.log (v)}) this.$event.$emit (' test', 1)

By the way, it encapsulates a mixin, and the advantage is that after the vue page listens to the event, after the page is destroyed, the event listener is automatically destroyed.

VuexVuex introduction

Vuex is a state management model developed specifically for Vue.js applications. It uses centralized storage to manage the state of all components of the application, and uses corresponding rules to ensure that the state changes in a predictable way.

Vuex solves the problem that multiple views depend on the same state and the behavior from different views need to change the same state, and focuses the developer's energy on the update of data rather than on the transfer of data between components.

Each module of Vuex

State: used for data storage and is the only data source in store

Getters: like the computational attributes in vue, based on the secondary packaging of state data, it is often used for data filtering and correlation calculation of multiple data.

Mutations: similar to a function, the only way to change state data, and cannot be used to handle asynchronous events

Actions: similar to mutation, it is used to submit a mutation to change the state without changing the state directly, and can include any asynchronous operation

Modules: similar to namespaces, it is used to define and manipulate the state of each module separately in a project for easy maintenance.

Application of Vuex instance

Here, let's create a new store folder and do some encapsulation of Vuex.

Add the index.js file under the store folder

/ / index.js// automatic mount storeimport Vue from 'vue'import Vuex from' vuex'Vue.use (Vuex) files under the specified directory let modules = {} / / @ / store/module are automatically mounted as store module const subModuleList = require.context ('@ / store/modules', false, / .js $/) subModuleList.keys () .forEach (subRouter = > {const moduleName = subRouter.substring (2) SubRouter.length-3) modules [moduleName] = subModuleList (subRouter) .default}) export default new Vuex.Store ({state: {}, mutations: {}, actions: {}, modules})

Add a module folder under the store folder, and then create a new user.js file in the module folder

/ / user.jsimport user from'@ / utils/user.js'import userApi from'@ / apis/user'import {OPEN_ACCOUNT_STAGE, STAGE_STATUS} from'@ / constant'let getUserPromise = nullexport default {namespaced: true, state () {return {userInfo: null, / / user information isLogined:! user.getToken (), / / whether you have logged in}}, mutations: {/ / update user information updateUser (state) Payload) {state.isLogined =!! payload state.userInfo = payload},}, actions: {/ / get current user information async getUserInfo (context) Payload) {/ / forceUpdate indicates whether to forcibly update if (context.state.userInfo & &! payload?.forceUpdate) {return context.state.userInfo} if (! getUserPromise | | payload?.forceUpdate) {getUserPromise = userApi.getUserInfo ()} / / get user information try {const userInfo = await getUserPromise context.commit ('updateUser') UserInfo)} finally {getUserPromise = null} return context.state.userInfo}, / / Logout async logout (context, payload = {}) {/ / whether to manually exit const {manual} = payload if (manual) {await userApi.postLogout ()} user.clearToken () context.commit ('updateUser', null)},}}

Then introduce it into the main.js file of the project

Import Vue from 'vue'import App from' @ / app.vue'import {router} from'@ / router'import store from'@ / store/index'const vue = new Vue ({el:'# app', name: 'root', router, store, render: h = > h (App),})

The package is very pleasant, and then it can be operated normally.

This.$store.state.user.isLoginedthis.$store.state.user.userInfothis.$store.commit ('user/updateUser', {}) await this.$store.dispatch (' user/logout', {manual: true}) localStorage / sessionStorage

This kind of communication is relatively simple, but the disadvantage is that the data and state are chaotic and not easy to maintain.

Get data through window.localStorage.getItem (key)

Storing data through window.localStorage.setItem (key,value)

Note that JSON.parse () / JSON.stringify () is used for data format conversion, localStorage / sessionStorage can be combined with vuex to achieve persistent preservation of data, and vuex is used to solve the problem of data and state confusion.

Implement a simple Store pattern by yourself

For small projects, communication is very simple, when using Vuex will appear redundant and tedious, in this case, it is best not to use Vuex, you can implement a simple Store in the project.

/ store.jsconst store = {debug: true, state: {author: 'yushihuxing'}, setAuthorAction (newValue) {if (this.debug) console.log ('setAuthorAction triggered with', newValue) this.state.author = newValue}, deleteAuthorAction () {if (this.debug) console.log (' deleteAuthorAction triggered') this.state.author =''} export default store

The principle of the above code is that the store.js file exposes an object store, and the store object is maintained together by introducing pages of the store.js file.

Like Vuex, state changes in store are triggered by action within store, and trigger marks can be printed through console.log (). This approach is ideal for small projects that do not need to use Vuex.

Compared with the method of $root accessing the root instance, this centralized state management method can determine how the current change is triggered by console.log () records during debugging, and it is easier to locate the problem.

Access the root instance through $root

With $root, any component can get the root Vue instance of the current component tree, and data sharing between components can be achieved by maintaining the data on the root instance.

/ / main.js root instance new Vue ({el:'# app', store, router, / / the data attribute of the root instance, maintaining the common data data: function () {return {author:'}}, components: {App}, template:'',}) Export default {created () {this.$root.author ='so'}} author of this article {{$root.author}}

Note: although communication can be achieved in this way, any data change at any time in any part of the application will not leave a record of the change, which is fatal for slightly complex applications. It is not recommended to use it in practical applications.

Attrs and listeners

Now let's talk about a situation where components An and D in the component diagram we gave at the beginning are intergenerational, so how did they communicate before?

Props binding is used to transmit information at one level. If a state change in the D component requires data to be passed to A, the event system is used to pass it up.

Using eventBus, it is still suitable for use in this case, but when it comes to multi-person cooperative development, the code is less maintainable and readable.

Use Vuex for data management, but if you just pass the data without intermediate processing, using Vuex processing feels a bit overqualified.

So you have $attrs / $listeners, which is usually used with inheritAttrs.

InheritAttrs

By default, parent-scoped attribute bindings (attribute bindings) that are not considered props will be "fallback" and applied to the root element of the child component as a normal HTML attribute. When writing a component that wraps a target element or another component, this may not always behave as expected.

By setting inheritAttrs to false, these default behaviors will be removed. These attribute can be made effective through the instance property $attrs, which is also added in 2.4. and can be explicitly bound to non-root elements through v-bind.

Note: this option does not affect class and style bindings.

It's an official description: it's still hard to understand.

To put it simply,

Inherit all properties except props when inheritAttrs:true

InheritAttrs:false inherits only class and style attributes

Attrs: contains feature bindings in the parent scope that are not considered (and not expected to be) props (with the exception of class and style), and internal components can be passed in via vMub bind = "$attrs". When a component does not declare any props, it contains bindings for all parent scopes (except class and style).

Listeners: contains the v-on event listeners in the parent scope (without the .native modifier). It can be passed in to internal components via vMuon = "$listeners". It is an object that contains all the event listeners acting on this component, which is equivalent to the event that the child component inherits from the parent component.

With so many textual concepts, let's look at code examples:

Create a new father.vue component

Import Child from'.. / components/child.vue' export default {name: 'father', components: {Child}, data () {return {name:' A Li Wang', age: 22, infoObj: {from: 'Guangdong' Job: 'policeman', hobby: [' reading', 'writing',' skating']}, methods: {updateInfo () {console.log ('update info') }, delInfo () {console.log ('delete info');}

Child.vue components:

Import GrandSon from'.. / components/grandSon.vue' export default {name: 'child', components: {GrandSon}, props: [' name'], data () {return {height: '180cm, weight:' 70kg'} }, created () {console.log (this.$attrs); / / result: age, infoObj, because three values of name, age and infoObj are sent from the parent component, and because name is received by props, there are only age, infoObj attributes console.log (this.$listeners) / / updateInfo: F, delInfo: F}, methods: {addInfo () {console.log ('add info')}

GrandSon.vue component

{{$attrs}}-{{$listeners}} export default {props: ['weight'], created () {console.log (this.$attrs); / / age, infoObj, height console.log (this.$listeners) / / updateInfo: F, delInfo: F, addInfo: f this.$emit (' updateInfo') / / can trigger updateInfo functions in father components}

Although the value transfer in this way is not often used, it does not feel very readable. However, it is deeply nested at the component level, and it will be tedious to use props, or when the project is relatively small and not suitable for using Vuex, you can consider using it.

That's all for "how to communicate between vue components". Thank you for reading. If you want to know more about the industry, you can follow the website, the editor will output more high-quality practical articles for you!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report