Lesson 7 value transfer between components

In the vue framework, the concept of component runs through. All pages in the project are a component with relatively independent scope. If data transfer is required between them, it must comply with certain rules, such as parent-child component value transfer, brother component value transfer, and intergenerational component value transfer (there are several components between them). Next, I will explain how to transfer values between these components:

 1. Pass values from parent component to child component (props)

New Src / views / father Vue file as parent component

father.vue

<template>

 <div class="father">

 </div>

</template>

<script>

export default {

 data () {

 return {

 }

 }

}

</script>

Create a new Src / components / children Vue files as subcomponents

children.vue

<template>

 <div class="children">

 <ul>

 <li v-for="(item, index) in userList" :key="index">{{item}}</li>

 </ul>

 </div>

</template>

<script>

export default {

 props: {

 userList: {

 type: Array,

 required: true

 }

 },

 data () {

 return {

 }

 }

}

</script>

Notice that there is a props attribute in the child component, which is used to receive the value passed by the parent component

The parent component refers to the child component and passes the value to the child component. The code is as follows:

father.vue

<template>

 <div class="father">

 <children-item :userList="userList"></children-item>

 </div>

</template>

<script>

import childrenItem from '_c/children' //Introducing sub components

export default {

 components: {childrenItem}, //Call subcomponent

 data () {

 return {

 userList: ['Zhang San', 'Li Si', 'WangTwo '] //Pass values to subcomponents

 }

 }

}

</script>

 2. Child component passes value to parent component ($emit)

In the sub component children Add a method sendData in Vue

children.vue

<template>

 <div class="children">

 <ul>

 <li v-for="(item, index) in userList" :key="index">{{item}}</li>

 </ul>

 <button @click="sendData">Pass value to parent component: 3</button>

 </div>

</template>

<script>

export default {

 props: {

 userList: {

 type: Array,

 required: true

 }

 },

 data () {

 return {

 }

 },

 methods: {

 sendData () {

 this.$emit('sendData', 3)

 }

 }

}

</script>

Parent component father Vue receives the value passed by the sub component through the event

father.vue

<template>

 <div class="father">

 <children-item :userList="userList" @sendData="getData"></children-item>

 <p>{{value}}</p>

 </div>

</template>

<script>

import childrenItem from '_c/children'

export default {

 components: {childrenItem},

 data () {

 return {

 userList: ['Zhang San', 'Li Si', 'WangTwo '],

 value: 0

 }

 },

 methods: {

 getData (val) {

 //Receive the value passed by the subcomponent

 this.value = val

 }

 }

}

</script>

 3. Sibling components or intergenerational components pass values ($emit/$on)

This method uses an empty Vue instance as the central event bus (event center) to trigger and listen to events, and cleverly and lightly realizes the communication between any component, including parent-child, brother and cross level. However, when our project is relatively large, we can choose better state management vuex.

New Src / bus / index JS file:

import Vue from 'vue'

const Bus = new Vue()

export default Bus

In main JS:

import Vue from 'vue'

import App from './App.vue'

import router from './router'

import store from './store'

import Bus from './bus/index' //Introduction bus

Vue.config.productionTip = false

Vue.prototype.$bus = Bus //Use bus

new Vue({

 router,

 store,

 render: h => h(App)

}).$mount('#app')

Create Src / components / brother1 Vue and brother2 The Vue file is used as a sibling component, allowing brother1 to pass values to brother2

brother1.vue

<template>

 <div class="brother1">

 <h3>Brother assembly 1</h3>

 <button @click="sendData">Brother assembly 1</button>

 </div>

</template>

<script>

export default {

 data () {

 return {

 }

 },

 methods: {

 sendData () {

 this.$bus.$emit('sendData', 1) //Send

 }

 }

}

</script>

brother2.vue

<template>

 <div class="brother2">

 <h3>Brother assembly 2</h3>

 <p>{{value}}</p>

 </div>

</template>

<script>

export default {

 data () {

 return {

 value: null

 }

 },

 methods: {

 },

 mounted () {

 this.$bus.$on('sendData', val => { //Receive

 console.log(val)

 this.value = val

 })

 }

}

</script>

 4. $attrs/$listeners

I haven't used this method in the project. It's vue2 Click on the example of how to use it in the new version of the tutorial, and then use it for me:

Here you need to create a new sub component, srccomponentschildren2 vue

After the construction, we will transform srcviewsfather Vue, and srccomponentschildren Vue this component

father.vue

<template>

 <div class="father">

 <div style="width: 300px;height: 300px;border: 1px solid red;margin: 0 auto;">

 <h4>Parent component</h4>

 <children-item></children-item>

 </div>

 </div>

</template>

<script>

import childrenItem from '_c/children'

export default {

 components: {childrenItem},

 data () {

 return {

 }

 },

 methods: {

 }

}

</script>

<style lang="less" scoped>

 .father {

 height: 300px;

 background: gold;

 }

</style>

children.vue

<template>

 <div class="children" style="margin: 30px;border:1px solid black;height: calc(100% - 90px);">

 <h4>Subcomponents-1</h4>

 <children-item2></children-item2>

 </div>

</template>

<script>

const childrenItem2 = () => import('_c/children2')

export default {

 components: {childrenItem2},

 data () {

 return {

 }

 },

 methods: {

 }

}

</script>

children2.vue

<template>

 <div class="children" style="margin: 30px;border:1px solid black;height: calc(100% - 90px);">

 <h4>Subcomponents-2</h4>

 </div>

</template>

<script>

export default {

 data () {

 return {

 }

 },

 methods: {

 }

}

</script>

The purpose of this is to make the relationship between them as follows:

The parent (a) component introduces children (b) as a sub component, and the children (b) component introduces children2 (c) as a sub component

father Components( A)--son--children Components( B)--son--children2 Components( C)

b is a sub component of a and c is a sub component of b, which forms a nested relationship. Now there is a requirement that component a needs to pass the value directly to component c. how many solutions are there?

  1. Vuex (overqualified)
  2. a first passes the value to b through props, and b then passes the value to c through props (error prone)
  3. Use the event bus $bus mentioned above (when multiple people cooperate to develop, the code maintainability is low)

Therefore, $attrs/$listeners is recommended

If there are name1 and name2 values in parent component a, they need to be passed to child component b

a component

<template>

 <div class="father">

 <div style="width: 300px;height: 300px;border: 1px solid red;margin: 0 auto;">

 <h4>Parent component</h4>

 <children-item :name1="name1" :name2="name2"></children-item>

 </div>

 </div>

</template>

<script>

import childrenItem from '_c/children'

export default {

 components: {childrenItem},

 data () {

 return {

 name1: 'Zhang San',

 name2: 'Li Si'

 }

 },

 methods: {

 }

}

</script>

After getting the value, sub component b passes it to component c through $attrs

b component

<template>

 <div class="children" style="margin: 30px;border:1px solid black;height: calc(100% - 100px);">

 <h4>Subcomponents-1</h4>

 {{$attrs}}

 <children-item2 v-bind="$attrs"></children-item2>

 </div>

</template>

<script>

const childrenItem2 = () => import('_c/children2')

export default {

 components: {childrenItem2},

 inheritAttrs: false,

 data () {

 return {

 }

 },

 methods: {

 }

}

</script>

The two values are obtained in the c component as follows:

c component

<template>

 <div class="children" style="margin: 30px;border:1px solid black;height: calc(100% - 90px);">

 <h4>Subcomponents-2</h4>

 <span>{{name1}}</span>

 <span>{{name2}}</span>

 </div>

</template>

<script>

export default {

 props: ['name1', 'name2'],

 inheritAttrs: true,

 data () {

 return {

 }

 },

 methods: {

 }

}

</script>

If component b receives the value name1 through props, component c will not receive the value name1

b component

<template>

 <div class="children" style="margin: 30px;border:1px solid black;height: calc(100% - 100px);">

 <h4>Subcomponents-1</h4>

 {{$attrs}}

 <children-item2 v-bind="$attrs"></children-item2>

 </div>

</template>

<script>

const childrenItem2 = () => import('_c/children2')

export default {

 components: {childrenItem2},

 props: ['name1'],

 inheritAttrs: false,

 data () {

 return {

 }

 },

 methods: {

 }

}

</script>

c component

<template>

 <div class="children" style="margin: 30px;border:1px solid black;height: calc(100% - 90px);">

 <h4>Subcomponents-2</h4>

 <span>{{name2}}</span>

 </div>

</template>

<script>

export default {

 props: ['name2'],

 inheritAttrs: true,

 data () {

 return {

 }

 },

 methods: {

 }

}

</script>

The above is about how to pass the value of component a to component c, which is passed down. So how to pass the value of component c to component a and up?

c component

<template>

 <div class="children" style="margin: 30px;border:1px solid black;height: calc(100% - 90px);">

 <h4>Subcomponents-2</h4>

 <span>{{name2}}</span>

 <button @click="sendData">towards a Component value transfer</button>

 </div>

</template>

<script>

export default {

 props: ['name2'],

 inheritAttrs: true,

 data () {

 return {

 }

 },

 methods: {

 sendData () {

 this.$emit('sendData', 1)

 }

 }

}

</script>

b component

<template>

 <div class="children" style="margin: 30px;border:1px solid black;height: calc(100% - 100px);">

 <h4>Subcomponents-1</h4>

 {{$attrs}}

 <children-item2 v-bind="$attrs" v-on="$listeners"></children-item2>

 </div>

</template>

<script>

const childrenItem2 = () => import('_c/children2')

export default {

 components: {childrenItem2},

 props: ['name1'],

 inheritAttrs: false,

 data () {

 return {

 }

 },

 methods: {

 }

}

</script>

a component

<template>

 <div class="father">

 <div style="width: 300px;height: 300px;border: 1px solid red;margin: 0 auto;">

 <h4>Parent component</h4>

 <span>{{value}}</span>

 <children-item :name1="name1" :name2="name2" @sendData="getData"></children-item>

 </div>

 </div>

</template>

<script>

import childrenItem from '_c/children'

export default {

 components: {childrenItem},

 data () {

 return {

 name1: 'Zhang San',

 name2: 'Li Si',

 value: null

 }

 },

 methods: {

 getData (val) {

 this.value = val

 }

 }

}

</script>

From the above code example, we can see the function of $attrs/$listeners. It is like a bridge, responsible for directly transmitting and receiving data in a and c

 5. provide/inject

In the fourth way: $attrs/$listeners, we can also see that the limitation of use is that the three components a, b and c must be nested at different levels. By using $attrs/$listeners in component b, a and c can communicate with each other across generations

provide/inject here is more flexible. It is vue2 New content in version 2, whether it is the nested relationship of a,b and c, or a,b,c,d,e Deeper nesting relationship, a component shares data through provide, b,c,d,e You can get the data shared by component a through inject

Let's take a look at the specific example code:

Let me demonstrate with the above components:

srcviewsfather.vue -- a assembly

srccomponentschildren.vue -- b assembly

srccomponentschildren2.vue -- c assembly

Use provide in a component

<template>

 <div class="father">

 <div style="width: 300px;height: 300px;border: 1px solid red;margin: 0 auto;">

 <h4>Parent component</h4>

 <children-item></children-item>

 </div>

 </div>

</template>

<script>

import childrenItem from '_c/children'

export default {

 components: {childrenItem},

 provide () {

 return {

 name1: this.name1,

 name2: this.name2

 }

 },

 data () {

 return {

 name1: 'Zhang San',

 name2: 'Li Si',

 }

 },

 methods: {

 }

}

</script>

b component use inject

<template>

 <div class="children" style="margin: 30px;border:1px solid black;height: calc(100% - 100px);">

 <h4>Subcomponents-1</h4>

 {{name1}}

 {{name2}}

 <children-item2></children-item2>

 </div>

</template>

<script>

const childrenItem2 = () => import('_c/children2')

export default {

 components: {childrenItem2},

 inject: ['name1', 'name2'],

 data () {

 return {

 }

 },

 methods: {

 }

}

</script>

c component use inject

<template>

 <div class="children" style="margin: 30px;border:1px solid black;height: calc(100% - 90px);">

 <h4>Subcomponents-2</h4>

 <span>{{name1}}</span>

 <span>{{name2}}</span>

 </div>

</template>

<script>

export default {

 inject: ['name1', 'name2'],

 data () {

 return {

 }

 },

 methods: {

 }

}

</script>

Isn't it simple, but! It should be noted that the binding of provide , and , inject , is not responsive. That is to say, if a value is modified in component A, the latter component cannot get the modified value. For example, modify the name1 value in component A:

<template>

 <div class="father">

 <div style="width: 300px;height: 300px;border: 1px solid red;margin: 0 auto;">

 <h4>Parent component</h4>

 <span>{{name1}}</span>

 <span>{{name2}}</span>

 <children-item></children-item>

 <button @click="changeName1">change name1 value</button>

 </div>

 </div>

</template>

<script>

import childrenItem from '_c/children'

export default {

 components: {childrenItem},

 provide () {

 return {

 name1: this.name1,

 name2: this.name2

 }

 },

 data () {

 return {

 name1: 'Zhang San',

 name2: 'Li Si',

 }

 },

 methods: {

 changeName1 () {

 this.name1 = 'Wangcai'

 }

 }

}

</script>

After name1 is changed, the values in B and C components are still modified

So is there any way to realize data response? Yes, see the following code example:

After the transformation of component a, it is equivalent to sharing the whole instance of component a

<template>

 <div class="father">

 <div style="width: 300px;height: 300px;border: 1px solid red;margin: 0 auto;">

 <h4>Parent component</h4>

 <span>{{name1}}</span>

 <span>{{name2}}</span>

 <children-item></children-item>

 <button @click="changeName1">change name1 value</button>

 </div>

 </div>

</template>

<script>

import childrenItem from '_c/children'

export default {

 components: {childrenItem},

 provide () {

 return {

 name: this //Provide this component instance to the following sub components

 }

 },

 data () {

 return {

 name1: 'Zhang San',

 name2: 'Li Si',

 }

 },

 methods: {

 changeName1 () {

 this.name1 = 'Wangcai'

 }

 }

}

</script>

b component

<template>

 <div class="children" style="margin: 30px;border:1px solid black;height: calc(100% - 100px);">

 <h4>Subcomponents-1</h4>

 {{name.name1}}

 <children-item2></children-item2>

 </div>

</template>

<script>

const childrenItem2 = () => import('_c/children2')

export default {

 components: {childrenItem2},

 inject: {

 name: {

 default: () => ({})

 }

 },

 data () {

 return {

 }

 },

 methods: {

 },

}

</script>

c component

<template>

 <div class="children" style="margin: 30px;border:1px solid black;height: calc(100% - 90px);">

 <h4>Subcomponents-2</h4>

 <span>{{name.name1}}</span>

 </div>

</template>

<script>

export default {

 inject: {

 name: {

 default: () => ({})

 }

 },

 data () {

 return {

 }

 },

 methods: {

 },

}

</script>

The above can realize the responsive transmission of data to sub components

As an aside, methods 4 and 5 are not commonly used APIs. In fact, it may be good to use about 50% of the APIs provided by vue when doing projects, but what I want to say is that even if they are not commonly used, we should know how to use them, and it is better to use them when doing projects

 5. $parent/$children/$refs

Using these three methods will get the component instance, and then you can directly call the methods or data in the component

Similarly, use the above three components a, B and C as examples to demonstrate:

a component

<template>

 <div class="father">

 <div style="width: 300px;height: 300px;border: 1px solid red;margin: 0 auto;">

 <h4>Parent component</h4>

 <span>{{name1}}</span>

 <span>{{name2}}</span>

 <children-item ref="childrenItem"></children-item>

 </div>

 </div>

</template>

<script>

import childrenItem from '_c/children'

export default {

 components: {childrenItem},

 provide () {

 return {

 name: this

 }

 },

 data () {

 return {

 name1: 'Zhang San',

 name2: 'Li Si',

 }

 },

 methods: {

 },

 mounted () {

 console.log(this.$refs.childrenItem.name)

 this.$refs.childrenItem.getName()

 //Same effect

 console.log(this.$children[0].name)

 this.$children[0].getName()

 }

}

</script>

b component

<template>

 <div class="children" style="margin: 30px;border:1px solid black;height: calc(100% - 100px);">

 <h4>Subcomponents-1</h4>

 <children-item2 ref="childrenItem2"></children-item2>

 </div>

</template>

<script>

const childrenItem2 = () => import('_c/children2')

export default {

 components: {childrenItem2},

 data () {

 return {

 name: 'Subcomponents-1'

 }

 },

 methods: {

 getName () {

 console.log(this.name)

 }

 },

 mounted () {

 console.log(this.$parent.name1) //Zhang San

 console.log(this.$refs.childrenItem2.name) //Sub assembly-2

 console.log(this.$children[0].name) //Sub assembly-2

 }

}

</script>

c component

<template>

 <div class="children" style="margin: 30px;border:1px solid black;height: calc(100% - 90px);">

 <h4>Subcomponents-2</h4>

 </div>

</template>

<script>

export default {

 data () {

 return {

 name: 'Subcomponents-2'

 }

 },

 methods: {

 getName () {

 console.log(this.name)

 }

 },

 mounted () {

 console.log(this.$parent.name) //Sub Assembly-1

 }

}

</script>

Tags: Front-end Vue.js

Posted by stelthius on Sun, 15 May 2022 09:30:54 +0300