Eight ways of Vue component communication

1. props / $emit

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

1. Pass value from parent component to child component

The following is an example to illustrate how the parent component passes data to the child component: how to obtain the data in the parent component section.vue in the child component article.vue articles: ['Dream of Red Mansions', 'Journey to the West', 'Romance of the Three Kingdoms']

在这里插入代码片
// section父组件
<template>
 <div class="section">
 //子组件元素--插入
 <com-article :articles="articleList"></com-article>
 </div>
</template>
 
<script>
import comArticle from './test/article.vue' //引入子组件
export default {
    
    
 name: 'HelloWorld',
 components: {
    
     comArticle },
 data() {
    
    
 return {
    
    
 articleList: ['红楼梦', '西游记', '三国演义']
 }
 }
}
</script>
在这里插入代码片
// 子组件 article.vue
<template>
 <div>
 <span v-for="(item, index) in articles" :key="index">{
    
    {
    
    item}}</span>//即是红楼梦 西游记 三国演义
 </div>
</template>
 
<script>
export default {
    
    
 props: ['articles']
}
</script>

Summary: prop can only be passed from the upper-level component to the lower-level component (parent-child component), which is the so-called one-way data flow. Moreover, prop is read-only and cannot be modified. All modifications will fail and warn.

2. The child component passes the value to the parent component

My own understanding of $emit is this: $emit is bound to a custom event. When this statement is executed, the parameter arg will be passed to the parent component, and the parent component will listen and receive parameters through v-on. Through an example, illustrate how the child component passes data to the parent component.
On the basis of the previous example, click on the item of the article rendered by the page, and the subscript displayed in the array in the parent component

在这里插入代码片
// 父组件中
<template>
 <div class="section">
 <com-article :articles="articleList" @onEmitIndex="onEmitIndex"></com-article>
 <p>{
    
    {
    
    currentIndex}}</p>
 </div>
</template>
 
<script>
import comArticle from './test/article.vue'
export default {
    
    
 name: 'HelloWorld',
 components: {
    
     comArticle },
 data() {
    
    
 return {
    
    
 currentIndex: -1,
 articleList: ['红楼梦', '西游记', '三国演义']
 }
 },
 methods: {
    
    
 onEmitIndex(idx) {
    
    
 this.currentIndex = idx
 }
 }
}
</script>
在这里插入代码片
<template>
 <div>
 <div v-for="(item, index) in articles" :key="index" @click="emitIndex(index)">{
    
    {
    
    item}}</div>
 </div>
</template>
 
<script>
export default {
    
    
 props: ['articles'],
 methods: {
    
    
 emitIndex(index) {
    
    
 this.$emit('onEmitIndex', index)
 }
 }
}
</script>

二、 $children / $parent

The premise of use is to specify that the parent-child relationship has been established. The child instance can use this.parent to access the parent instance, the child instance is pushed into the parent instance of the parent instance, and the child instance is pushed into the parent instance'sP a r e n t accesses the parent instance , and the child instance is pushed into the children array of the parent instance
Note: Useparent and parent and parentparent and children Their main purpose is as a fallback method for accessing components . It is more recommended to use props and events to realize the communication between parent and child components

在这里插入代码片
// 父组件中
<template>
 <div class="hello_world">
 <div>{
    
    {
    
    msg}}</div>
 <com-a></com-a>
 <button @click="changeA">点击改变子组件值</button>
 </div>
</template>
 
<script>
import ComA from './test/comA.vue'
export default {
    
    
 name: 'HelloWorld',
 components: {
    
     ComA },
 data() {
    
    
 return {
    
    
  msg: 'Welcome'
 }
 },
 
 methods: {
    
    
 changeA() {
    
    
  // 获取到子组件A
  this.$children[0].messageA = 'this is new value'
 }
 }
}
</script>
在这里插入代码片
// 子组件中
<template>
 <div class="com_a">
 <span>{
    
    {
    
    messageA}}</span>
 <p>获取父组件的值为: {
    
    {
    
    parentVal}}</p>
 </div>
</template>
 
<script>
export default {
    
    
 data() {
    
    
 return {
    
    
  messageA: 'this is old'
 }
 },
 computed:{
    
    
 parentVal(){
    
    
  return this.$parent.msg;
 }
 }
}
</script>

Pay attention to the boundary conditions, such as taking parent on #app to get an instance of new V ue ( ), then taking parent on this instance to get an instance of new Vue(), taking this instance againWhat parent gets is the instance of new V u e ( ) , and on this instance , the parent gets undefined, and the bottom-level subcomponent gets children is an empty array . Also notice that getting children is an empty array. also noticec h i l d r e n is an empty array . Also note that the values ​​of parent andchildren are different, and the values ​​of children are different,The value of c h i l d r e n is different , the value of children is an array, and $parent is an object

Summary
The above two methods are used for communication between parent-child components, and it is more common to use props for parent-child component communication; neither of them can be used for communication between non-parent-child components.

Three, provide/inject

Concept:
provide/inject is a new api of vue2.2.0. Simply put, variables are provided through provide in the parent component, and then injected into variables through inject in the child component.

Note: No matter how deep the child component is nested, as long as inject is called, the data in the provide can be injected, and it is not limited to returning data from the props attribute of the current parent component

Example verification
Next, use an example to verify the above description:
Suppose there are three components: A.vue, B.vue, C.vue where C is a subcomponent of B, and B is a subcomponent of A

在这里插入代码片
// A.vue
 
<template>
 <div>
    <comB></comB>
 </div>
</template>
 
<script>
 import comB from '../components/test/comB.vue'
 export default {
    
    
 name: "A",
 provide: {
    
    
  for: "demo"
 },
 components:{
    
    
  comB
 }
 }
</script>
在这里插入代码片
// B.vue
 
<template>
 <div>
 {
    
    {
    
    demo}}
 <comC></comC>
 </div>
</template>
 
<script>
 import comC from '../components/test/comC.vue'
 export default {
    
    
 name: "B",
 inject: ['for'],
 data() {
    
    
  return {
    
    
  demo: this.for
  }
 },
 components: {
    
    
  comC
 }
 }
</script>
在这里插入代码片
// C.vue
<template>
 <div>
 {
    
    {
    
    demo}}
 </div>
</template>
 
<script>
 export default {
    
    
 name: "C",
 inject: ['for'],
 data() {
    
    
  return {
    
    
  demo: this.for
  }
 }
 }
</script>

Four, ref / refs

ref: If it is used on a normal DOM element, the reference points to the DOM element; if it is used on a sub-component, the reference points to the component instance, which can directly call the method of the component or access the data through the instance. Let's look at an example of ref to access the component:

在这里插入代码片

// 子组件 A.vue
 
export default {
    
    
 data () {
    
    
 return {
    
    
  name: 'Vue.js'
 }
 },
 methods: {
    
    
 sayHello () {
    
    
  console.log('hello')
 }
 }
}
在这里插入代码片
// 父组件 app.vue
 
<template>
 <component-a ref="comA"></component-a>
</template>
<script>
 export default {
    
    
 mounted () {
    
    
  const comA = this.$refs.comA;
  console.log(comA.name); // Vue.js
  comA.sayHello(); // hello
 }
 }
</script>

Five, eventBus

eventBus, also known as event bus, can be used as a communication bridge concept in Vue, just like all components share the same event center, and can register to send events or receive events to the center, so components can notify other components.

eventBus is also inconvenient. When the project is large, it is easy to cause a disaster that is difficult to maintain

How to use eventBus to realize data communication between components in Vue project? Specifically, through the following steps

1. Initialization

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

在这里插入代码片
// event-bus.js
 
import Vue from 'vue'
export const EventBus = new Vue()

2. Send event

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

在这里插入代码片
<template>
 <div>
  <show-num-com></show-num-com>
  <addition-num-com></addition-num-com>
 </div>
</template>
 
<script>
import showNumCom from './showNum.vue'
import additionNumCom from './additionNum.vue'
export default {
    
    
 components: {
    
     showNumCom, additionNumCom }
}
</script>
在这里插入代码片
// addtionNum.vue 中发送事件
 
<template>
 <div>
  <button @click="additionHandle">+加法器</button>  
 </div>
</template>
 
<script>
import {
    
    EventBus} from './event-bus.js'
console.log(EventBus)
export default {
    
    
 data(){
    
    
  return{
    
    
   num:1
  }
 },
 
 methods:{
    
    
  additionHandle(){
    
    
   EventBus.$emit('addition', {
    
    
    num:this.num++
   })
  }
 }
}

3. Receive events

在这里插入代码片
// showNum.vue 中接收事件
 
<template>
 <div>计算和: {
    
    {
    
    count}}</div>
</template>
 
<script>
import {
    
     EventBus } from './event-bus.js'
export default {
    
    
 data() {
    
    
  return {
    
    
   count: 0
  }
 },
 
 mounted() {
    
    
  EventBus.$on('addition', param => {
    
    
   this.count = this.count + param.num;
  })
 }
}
</script>

In this way, the addition button is clicked in the component addtionNum.vue, and the summation result is displayed in showNum.vue using the passed num.

  1. Remove the event listener
    If you want to remove the event listener, you can do the following:
在这里插入代码片
import {
    
     eventBus } from 'event-bus.js'
EventBus.$off('addition', {
    
    })

6. Vuex

1. Introduction to Vuex

Vuex is a state management pattern 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 behaviors from different views need to change the same state, focusing developers on data updates rather than data transfer between
components

2. Each module of Vuex

  • state: used for data storage, is the only data source in the store
  • Getters: Like the calculated properties in Vue, based on the secondary packaging of state data, it is often used for data screening and correlation calculation of multiple data
  • mutations: Similar to functions, the only way to change state data, and cannot be used to handle asynchronous events
  • actions: Similar to mutation, it is used to submit mutation to change the state without directly changing the state, and can contain any asynchronous operation
  • modules: Similar to a namespace, it is used to define and operate the state of each module separately in the project for easy maintenance

3. Vuex example application

在这里插入代码片
// 父组件
 
<template>
 <div id="app">
  <ChildA/>
  <ChildB/>
 </div>
</template>
 
<script>
 import ChildA from './components/ChildA' // 导入A组件
 import ChildB from './components/ChildB' // 导入B组件
 
 export default {
    
    
  name: 'App',
  components: {
    
    ChildA, ChildB} // 注册A、B组件
 }
</script>
在这里插入代码片
// 子组件childA
 
<template>
 <div id="childA">
  <h1>我是A组件</h1>
  <button @click="transform">点我让B组件接收到数据</button>
  <p>因为你点了B,所以我的信息发生了变化:{
    
    {
    
    BMessage}}</p>
 </div>
</template>
 
<script>
 export default {
    
    
  data() {
    
    
   return {
    
    
    AMessage: 'Hello,B组件,我是A组件'
   }
  },
  computed: {
    
    
   BMessage() {
    
    
    // 这里存储从store里获取的B组件的数据
    return this.$store.state.BMsg
   }
  },
  methods: {
    
    
   transform() {
    
    
    // 触发receiveAMsg,将A组件的数据存放到store里去
    this.$store.commit('receiveAMsg', {
    
    
     AMsg: this.AMessage
    })
   }
  }
 }
</script>
在这里插入代码片
// 子组件 childB
 
<template>
 <div id="childB">
  <h1>我是B组件</h1>
  <button @click="transform">点我让A组件接收到数据</button>
  <p>因为你点了A,所以我的信息发生了变化:{
    
    {
    
    AMessage}}</p>
 </div>
</template>
 
<script>
 export default {
    
    
  data() {
    
    
   return {
    
    
    BMessage: 'Hello,A组件,我是B组件'
   }
  },
  computed: {
    
    
   AMessage() {
    
    
    // 这里存储从store里获取的A组件的数据
    return this.$store.state.AMsg
   }
  },
  methods: {
    
    
   transform() {
    
    
    // 触发receiveBMsg,将B组件的数据存放到store里去
    this.$store.commit('receiveBMsg', {
    
    
     BMsg: this.BMessage
    })
   }
  }
 }
</script>

vuex-store.js

在这里插入代码片
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const state = {
    
    
 // 初始化A和B组件的数据,等待获取
 AMsg: '',
 BMsg: ''
}
 
const mutations = {
    
    
 receiveAMsg(state, payload) {
    
    
  // 将A组件的数据存放于state
  state.AMsg = payload.AMsg
 },
 receiveBMsg(state, payload) {
    
    
  // 将B组件的数据存放于state
  state.BMsg = payload.BMsg
 }
}
 
export default new Vuex.Store({
    
    
 state,
 mutations
})

七、localStorage / sessionStorage

This kind of communication is relatively simple, but the disadvantage is that the data and status are more chaotic, and it is not easy to maintain.
Get data through window.localStorage.getItem(key)
Store 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 storage of data, and use vuex to solve the problem of data and state confusion.

Eight $attrs and $listeners

If the component relationship given at the beginning is A component and D component are intergenerational, then what are the ways for them to communicate before?

  • Use props binding to carry out level-by-level information transfer. If the state change in D component needs to transfer data to A, use the event system to pass up level by level
  • Using eventBus is more suitable in this case, but when encountering multi-person cooperative development, the code maintainability is low and the readability is low
  • Use Vuex for data management, but if you just pass data without intermediate processing, using Vuex for processing feels a bit overkill.

In vue2.4, in order to solve this requirement, attrs and attrs anda t t r s and listeners, add inheritAttrs option. Prior to version 2.4, by default, attribute bindings (except class and style ) that were not recognized (and acquired) as props in the parent scope would "fall back" and be applied as normal HTML attributes to the root element of the child component. Let's look at an example of cross-level communication:

在这里插入代码片
// app.vue
// index.vue
 
<template>
 <div>
  <child-com1
   :name="name"
   :age="age"
   :gender="gender"
   :height="height"
   title="程序员成长指北"
  ></child-com1>
 </div>
</template>
<script>
const childCom1 = () => import("./childCom1.vue");
export default {
    
    
 components: {
    
     childCom1 },
 data() {
    
    
  return {
    
    
   name: "zhang",
   age: "18",
   gender: "女",
   height: "158"
  };
 }
};
</script>
在这里插入代码片
// childCom1.vue
 
<template class="border">
 <div>
  <p>name: {
    
    {
    
     name}}</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, // 可以关闭自动挂载到组件根元素上的没有在props声明的属性
 props: {
    
    
  name: String // name作为props属性绑定
 },
 created() {
    
    
  console.log(this.$attrs);
   // { "age": "18", "gender": "女", "height": "158", "title": "程序员成长指北" }
 }
};
</script>
在这里插入代码片
// childCom2.vue
 
<template>
 <div class="border">
  <p>age: {
    
    {
    
     age}}</p>
  <p>childCom2: {
    
    {
    
     $attrs }}</p>
 </div>
</template>
<script>
 
export default {
    
    
 inheritAttrs: false,
 props: {
    
    
  age: String
 },
 created() {
    
    
  console.log(this.$attrs); 
  // { "gender": "女", "height": "158", "title": "程序员成长指北" }
 }
};
</script>

Summarize

Common usage scenarios can be broken down into three categories:

  • Parent-child component communication: props; $parent / $children; provide / inject ; ref ; $attrs
    / $listeners
  • Brother component communication: eventBus ; vuex
  • Crossover communication: eventBus; Vuex; provide / inject, $attrs / $listeners

Guess you like

Origin blog.csdn.net/m0_44973790/article/details/124307915