Seven Ways Vue Realizes Communication Between Components

1. props / $emit

The parent component propspasses data to the child component through the method, and $emit the child component can communicate with the parent component through:

  • Passing from parent to child: The parent component introduces the child component through import, registers it, and adds the attributes to be passed on the child component label. The child component receives it through props. There are two forms of reception. One is through the array form ['properties to be received'] , the second is through the object form { }
  • Passing from child to parent: The parent component passes the event method to the child component, and the child component $emitcalls back to the parent component by triggering the event

The characteristics of props:

  • Props can only pass values ​​from parent components to child components, and props make a one-way downstream binding between parent and child components. The data of the child component will be updated responsively with the update of the parent component; but the child component cannot cause the data update of the parent component.
  • props can display and define one or more data. For the received data, it can be of various data types, and it can also pass an object or function.
  • props attribute name rule: If the camel case is used in props, the tags in the template need to be written in the form of dashes.

 Code example:

Father to son (usage of prop)

parent component:

<template>
    <div id="father">
        <son :msg="msg" :fn="myFunc"></son>
    </div>
</template>

<script>
import son from "./son.vue";
export default {
    name: "father",
    components: {
        son
    },
    data() {
        msg: "我是父组件";
    },
    methods: {
        myFunc() {
            console.log("我是父组件的方法");
        }
    }
};
</script>

Subassembly:

<template>
    <div id="son">
        <p>{
   
   {msg}}</p>
        <button @click="fn">按钮</button>
    </div>
</template>
<script>
export default {
    name: "son",
    props: ["msg", "fn"]
};
</script>

From son to father (usage of $emit)

$emit binds a custom event, when the event is executed, the parameters are passed to the parent component, and the parent component listens and receives parameters through v-on

parent component:

<template>
  <div id="father">
    <son :arrList="arrList" @changeIndex="changeIndex"></son>
    <p>{
   
   {currentIndex}}</p>
  </div>
</template>

<script>
import son from './son.vue'
export default {
  name: 'father',
  components: { son},
  data() {
    return {
      currentIndex: -1,
      arrList: ['龙族', '绘梨衣', '前端','后端']
    }
  },
  methods: {
    changeIndex(index) {
      this.currentIndex = index
    }
  }
}
</script>

Subassembly:

<template>
  <div>
    <div v-for="(item, index) in arrList" :key="index" @click="emitIndex(index)">{
   
   {item}}</div>
  </div>
</template>

<script>
export default {
  props: ['arrList'],
  methods: {
    emitIndex(index) {
      this.$emit('changeIndex', index) // 触发父组件的方法,并传递参数index
    }
  }
}
</script>

2.ref / $refs

ref: This attribute is used on a subcomponent, its reference points to the instance of the subcomponent, and the data and methods of the component can be accessed through the instance; if it is used on a normal DOM element, the reference points to the DOM element

parent component:

<template>
  <child ref="child"></component-a>
</template>
<script>
  import child from './child.vue'
  export default {
    components: { child },
    mounted () {
      console.log(this.$refs.child.name);  // mySon
      this.$refs.child.sayHello();  // Hello father!
    }
  }
</script>

Subassembly:

<template>
  <div id="app"></div>
</template>
<script>
export default {
  name:'child',
  data () {
    return {
      name: 'mySon'
    }
  },
  methods: {
    sayHello () {
      console.log('Hello father!')
    }
  }
}
</script>

3.eventBus (event bus)

The principle is: event subscription publishing, 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.

The steps to use are as follows:

(1) Create communication between event center management components

// event-bus.js

import Vue from 'vue'
export const EventBus = new Vue()

(2) Sending events Suppose there are two sibling components firstCom and secondCom:

Parent components of firstCom and secondCom:

<template>
  <div>
    <first-com></first-com>
    <second-com></second-com>
  </div>
</template>

<script>
import firstCom from './firstCom.vue'
import secondCom from './secondCom.vue'
export default {
  components: { firstCom, secondCom }
}
</script>

Send event in firstCom component:

<template>
  <div>
    <button @click="add">点击增加</button>    
  </div>
</template>

<script>
import {EventBus} from './event-bus.js' // 引入事件中心

export default {
  data(){
    return{
      num:0
    }
  },
  methods:{
    add(){
      EventBus.$emit('addition', {
        num:this.num++
      })
    }
  }
}
</script>

(3) Receive events

Receive events in the secondCom component:

<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 the above code, this is equivalent to storing the num value in the event bus, which can be directly accessed in other components. The event bus is equivalent to a bridge, and no components communicate through it. Although it seems relatively simple, this method also has some invariants. If the project is too large, using this method for communication will be difficult to maintain later.

4.Vuex

Vuex is a state management pattern developed specifically for Vue.js applications. It uses a 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 manner.

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' energy on updating data rather than passing data between components

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

Steps to use Vuex:

(1) Here we create a new store folder first, and perform some packaging processing on Vuex

Add index.js file under store folder

// index.js
 
// 自动挂载指定目录下的store
import Vue from 'vue'
import Vuex from 'vuex'
 
Vue.use(Vuex)
 
let modules = {}
 
// @/store/module 目录下的文件自动挂载为 store 模块
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
})
 

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

// user.js
 
import user from '@/utils/user.js'
import userApi from '@/apis/user'
import { OPEN_ACCOUNT_STAGE, STAGE_STATUS } from '@/constant'
 
let getUserPromise = null
 
export default {
  namespaced: true,
  state() {
    return {
      userInfo: null, // 用户信息
      isLogined: !!user.getToken(), // 是否已经登录
    }
  },
  mutations: {
    // 更新用户信息
    updateUser(state, payload) {
      state.isLogined = !!payload
      state.userInfo = payload
    },
  },
  actions: {
    // 获取当前用户信息
     getUserInfo(context, payload) {
      //相关代码
    },
 
    // 登出
     logout(context, payload = {}) {
      // 是否手动退出
      const { manual } = payload
      if (manual) {
        await userApi.postLogout()
      }
      user.clearToken()
      context.commit('updateUser', null)
    },
  }
}
 

main.js (3) Then introduce in the project  file

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),
})

(4) The package is happily finished, and then it can be operated normally

this.$store.state.user.isLogined
this.$store.state.user.userInfo
this.$store.commit('user/updateUser', {})
 await this.$store.dispatch('user/logout', { manual: true })

5. $attrs and $listeners

Now let's discuss another situation: If component A and component D in the component relationship diagram we give have an intergenerational relationship, what ways did they communicate before?

  1. 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
  2. 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
  3. Use Vuex for data management, but if you just pass data without intermediate processing, using Vuex for processing feels a bit overkill.

So there is $attrs / $listeners, usually used together with inheritAttrs.

inheritAttrs

By default attribute bindings in the parent scope that are not considered props will "fall back" and be applied as normal HTML attributes on the root element of the child component. This may not always behave as expected when composing a component that wraps one target element or another.

By setting inheritAttrs to false, these default behaviors will be removed. These attributes can take effect through the instance property $attrs, and can be explicitly bound to non-root elements through v-bind.

Note: This option does not affect class and style binding, Vue has special treatment for class and style

Simply put

  • inheritAttrs: when true, inherit all attributes except props
  • inheritAttrs: false only inherits class and style attributes
  • $attrs: Contains attribute bindings (except class and style) that are not considered (and not expected) props in the parent scope, and can be passed to internal components via v-bind="$attrs". When a component doesn't declare any props, it includes all parent scope bindings (except class and style).
  • $listeners: Contains the v-on event listeners in the parent scope (without the .native modifier). It can be passed to internal components via v-on="$listeners". It is an object that contains all event listeners that act on this component, which is equivalent to the child component inheriting the events of the parent component.

Code example:

parent component:

<template>
   <child :name="name" :age="age" :infoObj="infoObj" @updateInfo="updateInfo" @delInfo="delInfo" />
</template>
<script>
    import Child from '../components/child.vue'
    export default {
        name: 'father',
        components: { Child },
        data () {
            return {
                name: '绘梨衣',
                age: 22,
                infoObj: {
                    from: '河北',
                    job: 'superman',
                    hobby: ['reading', 'writing', 'eating']
                }
            }
        },
        methods: {
            updateInfo() {
                console.log('update info');
            },
            delInfo() {
                console.log('delete info');
            }
        }
    }
</script>

 Son component:

<template>
    <!-- 通过 $listeners 将父作用域中的事件,传入 grandSon 组件,使其可以获取到 father 中的事件 -->
    <grand-son :height="height" :weight="weight" @addInfo="addInfo" v-bind="$attrs" v-on="$listeners"  />
</template>
<script>
    import GrandSon from '../components/grandSon.vue'
    export default {
        name: 'child',
        components: { GrandSon },
        props: ['name'],
        data() {
          return {
              height: '170cm',
              weight: '55kg'
          };
        },
        created() {
            console.log(this.$attrs); 
       // 结果:age, infoObj, 因为父组件共传来name, age, infoObj三个值,由
             //于name被 props接收了,所以只有age, infoObj属性
            console.log(this.$listeners); // updateInfo: f, delInfo: f
        },
        methods: {
            addInfo () {
                console.log('add info')
            }
        }
    }
</script>

Grandchild component:

<template>
    <div>
        {
   
   { $attrs }} --- {
   
   { $listeners }}
    <div>
</template>
<script>
    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') 
          // 可以触发 father 组件中的updateInfo函数
        }
    }
</script>

6.$parent / $children

  • Using $parent allows the component to access the instance of the parent component (the properties and methods of the previous parent component are accessed).
  • Using $children allows components to access instances of child components, however, $children does not guarantee order, and the accessed data is not responsive.

Notice:

  • The instance accessed through $parent is the instance of the upper-level parent component, and $root can be used to access the instance of the root component
  • Use $children in the component to get the instances of all subcomponents, which is an array and unordered
  • Take $parent on the root component #app to get an instance of new Vue(), take $parent on this instance to get undefined, and get $children in the bottom subcomponent is an empty array
  • The value of $children is an array, while $parent is an object

usage:

Subassembly:

<template>
  <div>
    <span>{
   
   {message}}</span>
    <p>父组件的值为:  {
   
   {parentVal}}</p>
  </div>
</template>

<script>
export default {
  data() {
    return {
      message: 'Vue'
    }
  },
  computed:{
    parentVal(){
      return this.$parent.msg;
    }
  }
}
</script>

parent component:

<template>
  <div class="app">
    <div>{
   
   {msg}}</div>
    <child></child>
    <button @click="change">点击改变子组件值</button>
  </div>
</template>

<script>
import child from './child.vue'
export default {
  components: { child },
  data() {
    return {
      msg: 'Hello'
    }
  },
  methods: {
    change() {
      // 获取到子组件
      this.$children[0].message = 'JavaScript'
    }
  }
}
</script>

7. Dependency injection (provide/inject)

This method is dependency injection in vue, which is used for communication between parent and child components. Of course, the father and son mentioned here are not necessarily real father and son, but also grandparents and grandchildren. In the case of deep layers, this method can be used to pass values. There is no need to pass data layer by layer.

Provide and inject are two hooks provided by vue, which are at the same level as data and methods. And the written form of provide is the same as data.

  • The provide hook is used to send data or methods
  • The inject hook is used to receive data or methods

Note: The properties provided by dependency injection are not reactive.

usage:

parent component:

provide() { 
    return {     
        num: this.num  
    };
}

Subassembly:

inject: ['num']

There is another way of writing, which can access all properties in the parent component:

provide() {
 return {
    app: this
  };
}
data() {
 return {
    num: 111
  };
}

inject: ['app']
console.log(this.app.num)

Summarize

1. Communication between parent and child components

  • The child component accepts the data of the parent component through the props attribute, and then the parent component registers the listening event on the child component, and the child component sends data to the parent component through the emit trigger event.
  • Set a name for the child component via the ref attribute. The parent component obtains the child component through the $refs component name, and the child component obtains the parent component through $parent, so that communication can also be achieved.
  • Use provide/inject, provide variables through provide in the parent component, and inject variables into the component through inject in the child component. No matter how deep the subcomponent is, as long as inject is called, the data in provide can be injected

2. Communication between intergenerational components

Communication between inter-generational components is actually multi-layer communication between parent-child components. The above method of communication between parent-child components can also be used, but multi-layer communication will be more troublesome.

3. Communication between sibling components

Through $parent + $refs, the parent component is used as an intermediary to obtain sibling components, and communication is also possible.

4. Communication between arbitrary components

Using eventBus is actually creating an event center, which is equivalent to a transfer station, which can be used to transmit events and receive events. Its essence is to create an empty Vue instance as the object of message delivery, and the communication components introduce this instance, and the communication components realize message delivery by listening to and triggering events on this instance.

Guess you like

Origin blog.csdn.net/m0_65335111/article/details/127550835