Various component communication modes of Vue3 and Vue2

Vue3 communication uses writing method

1. props

Method 1: mixed writing

// Parent.vue transfer
<child :msg1="msg1" :msg2="msg2"></child>

<script>
import child from "./child.vue"
import { ref, reactive } from "vue"
export default {
    data(){
        return {
            msg1:"This is the message 1 of the pass level subassembly"
        }
    },
    setup(){
        // Create a responsive data
        
        // The writing method 1 is applicable to the basic type ref and has other uses, which are described in the following chapters
        const msg2 = ref("This is the message 2 of the pass level subassembly")
        
        // Writing method 2 is applicable to complex types, such as arrays and objects
        const msg2 = reactive(["This is the message 2 of the pass level subassembly"])
        
        return {
            msg2
        }
    }
}
</script>

// Child.vue receive
<script>
export default {
  props: ["msg1", "msg2"],// If this line is not written, it will not be received below
  setup(props) {
    console.log(props) // {msg1: "this is the information 1 passed to the sub component", msg2: "this is the information 2 passed to the sub component"}
  },
}
</script>

Method 2: pure Vue3 writing

// Parent.vue transfer
<child :msg2="msg2"></child>

<script setup>
    import child from "./child.vue"
    import { ref, reactive } from "vue"
    const msg2 = ref("This is the message passed to the subcomponent 2")
    // Or complex type
    const msg2 = reactive(["This is the message 2 of the pass level subassembly"])
</script>

// Child.vue receive
<script setup>
    // Direct use without introduction
    // import { defineProps } from "vue"
    const props = defineProps({
        // Writing method I
        msg2: String
        // Writing method 2
        msg2:{
            type:String,
            default:""
        }
    })
    console.log(props) // {msg2: "this is the information 2 of the pass level sub component"}
</script>

2. $emit

// Distributed by Child.vue
<template>
    // Writing method I
    <button @click="emit('myClick')">Button</buttom>
    // Writing method 2
    <button @click="handleClick">Button</buttom>
</template>

<script setup>
    
    // Method 1 is applicable to Vue3.2 and does not need to be introduced
    // import { defineEmits } from "vue"
    // Corresponding writing method I
    const emit = defineEmits(["myClick","myClick2"])
    // Correspondence 2
    const handleClick = ()=>{
        emit("myClick", "This is the information sent to the parent component")
    }
    
    // Method 2 is not applicable to Vue3.2, which is obsolete
    import { useContext } from "vue"
    const { emit } = useContext()
    const handleClick = ()=>{
        emit("myClick", "This is the information sent to the parent component")
    }
</script>

// Parent.vue response
<template>
    <child @myClick="onMyClick"></child>
</template>

<script setup>
    import child from "./child.vue"
    const onMyClick = (msg) => {
        console.log(msg) // This is the message received by the parent component
    }
</script>

3. expose / ref

The parent component obtains the properties of the child component or calls the child component method

// Child.vue
<script setup>
    // Method 1 is not applicable to Vue3.2, which is obsolete
    import { useContext } from "vue"
    const ctx = useContext()
    // External exposure properties and methods are OK
    ctx.expose({
        childName: "This is the property of the subcomponent",
        someMethod(){
            console.log("This is the sub component method")
        }
    })
    
    // Method 2 is applicable to Vue3.2 and does not need to be introduced
    // import { defineExpose } from "vue"
    defineExpose({
        childName: "This is the property of the subcomponent",
        someMethod(){
            console.log("This is the sub component method")
        }
    })
</script>

// Parent.vue note ref="comp"
<template>
    <child ref="comp"></child>
    <button @click="handlerClick">Button</button>
</template>
<script setup>
    import child from "./child.vue"
    import { ref } from "vue"
    const comp = ref(null)
    const handlerClick = () => {
        console.log(comp.value.childName) // Get the exposed properties of the subcomponent
        comp.value.someMethod() // Call the exposed method of the sub component
    }
</script>

4. attrs

attrs: contains a collection of non props attributes in the parent scope except class and style

// Parent.vue transfer
<child :msg1="msg1" :msg2="msg2" title="3333"></child>

<script setup>
    import child from "./child.vue"
    import { ref, reactive } from "vue"
    const msg1 = ref("1111")
    const msg2 = ref("2222")
</script>

// Child.vue receive
<script setup>
    import { defineProps, useContext, useAttrs } from "vue"
    // In version 3.2, defineProps does not need to be introduced and can be used directly
    const props = defineProps({
        msg1: String
    })
    // Method 1 is not applicable to Vue3.2, which is obsolete
    const ctx = useContext()
    // If you do not receive msg1 with props, it is {msg1: "1111", msg2:"2222", title: "3333"}
    console.log(ctx.attrs) // { msg2:"2222", title: "3333" }
    
    // Method 2 is applicable to Vue3.2
    const attrs = useAttrs()
    console.log(attrs) // { msg2:"2222", title: "3333" }
</script>

5. v-model

It can support multiple bidirectional data binding

// Parent.vue
<child v-model:key="key" v-model:value="value"></child>

<script setup>
    import child from "./child.vue"
    import { ref, reactive } from "vue"
    const key = ref("1111")
    const value = ref("2222")
</script>

// Child.vue
<template>
    <button @click="handlerClick">Button</button>
</template>

<script setup>
    
    // Method 1 is not applicable to Vue3.2, which is obsolete
    import { useContext } from "vue"
    const { emit } = useContext()
    
    // Method 2 is applicable to Vue3.2 and does not need to be introduced
    // import { defineEmits } from "vue"
    const emit = defineEmits(["key","value"])
    
    // usage
    const handlerClick = () => {
        emit("update:key", "new key")
        emit("update:value", "new value")
    }
</script>

6. provide / inject

provide / inject is dependency injection

Provide: allows us to specify the data or data that we want to provide to future generations of components

inject: receive the data you want to add to any descendant component. No matter how deep the component is nested, it can be used directly

// Parent.vue
<script setup>
    import { provide } from "vue"
    provide("name", "Mu Hua")
</script>

// Child.vue
<script setup>
    import { inject } from "vue"
    const name = inject("name")
    console.log(name) // Mu Hua
</script>

7. Vuex

// store/index.js
import { createStore } from "vuex"
export default createStore({
    state:{ count: 1 },
    getters:{
        getCount: state => state.count
    },
    mutations:{
        add(state){
            state.count++
        }
    }
})

// main.js
import { createApp } from "vue"
import App from "./App.vue"
import store from "./store"
createApp(App).use(store).mount("#app")

// Page.vue
// Method 1: direct use
<template>
    <div>{{ $store.state.count }}</div>
    <button @click="$store.commit('add')">Button</button>
</template>

// Method 2 acquisition
<script setup>
    import { useStore, computed } from "vuex"
    const store = useStore()
    console.log(store.state.count) // 1

    const count = computed(()=>store.state.count) // Responsive, which changes as vuex data changes
    console.log(count) // 1 
</script>

8. mitt

There is no cross component communication of EventBus in Vue3, but now there is an alternative scheme mit.js, and the principle is EventBus

Install NPM I MIT - s first

Then package the bus as before

mitt.js
import mitt from 'mitt'
const mitt = mitt()
export default mitt

Then the use of communication between the two components

// Component A
<script setup>
import mitt from './mitt'
const handleClick = () => {
    mitt.emit('handleChange')
}
</script>

// Component B 
<script setup>
import mitt from './mitt'
import { onUnmounted } from 'vue'
const someMethed = () => { ... }
mitt.on('handleChange',someMethed)
onUnmounted(()=>{
    mitt.off('handleChange',someMethed)
})
</script>

Vue2.x component communication mode

There are 12 kinds of Vue2.x component communicationParent child component communicationSibling component communicationCross level component communication
propspropsEventBusEventBus
$emit / v-on$emit / v-onVuexprovide/inject
.syncattrs/listeners$parentVuex
v-modelref attrs/listeners
ref.sync $root
children/parentv-model
attrs/listenerschildren/parent
provide / inject
EventBus
Vuex
$root
slot

Vue2.x communication usage

1. props

The parent component transmits data to the child component, which should be the most common way
After the child component receives the data, it cannot directly modify the data of the parent component. An error will be reported, so when the parent component re renders, the data will be overwritten. If you want to modify in a sub component, it is recommended to use computed

// Parent.vue transfer
<template>
    <child :msg="msg"></child>
</template>

// Child.vue receive
export default {
  // Write a receive with array
  props:['msg'],
  // The second writing method uses object reception, which can limit the received data type, set the default value, verify, etc
  props:{
      msg:{
          type:String,
          default:'This is the default data'
      }
  },
  mounted(){
      console.log(this.msg)
  },
}

2. .sync

It can help us realize the two-way binding of the data transmitted from the parent component to the child component, so the child component can modify the data directly after receiving the data, and will modify the data of the parent component at the same time

// Parent.vue
<template>
    <child :page.sync="page"></child>
</template>
<script>
export default {
    data(){
        return {
            page:1
        }
    }
}

// Child.vue
export default {
    props:["page"],
    computed(){
        // When we modify the currentPage in the child component, the page of the parent component will also change
        currentPage {
            get(){
                return this.page
            },
            set(newVal){
                this.$emit("update:page", newVal)
            }
        }
    }
}
</script>

3. v-model

Similar to. sync, the data passed from the parent component to the child component can be bidirectional bound, and the child component can modify the data of the parent component through $emit

// Parent.vue
<template>
    <child v-model="value"></child>
</template>
<script>
export default {
    data(){
        return {
            value:1
        }
    }
}

// Child.vue
<template>
    <input :value="value" @input="handlerChange">
</template>
export default {
    props:["value"],
    // You can modify the event name, which defaults to input
    model:{
        event:"updateValue"
    },
    methods:{
        handlerChange(e){
            this.$emit("input", e.target.value)
            // If there is a rename above, that's it
            this.$emit("updateValue", e.target.value)
        }
    }
}
</script>

4. ref

ref if it is on an ordinary DOM element, the reference refers to the DOM element;

If on a child component, the reference points to the child component instance, then the parent component can actively obtain the properties of the child component or call the methods of the child component through ref

// Child.vue
export default {
    data(){
        return {
            name:"Mu Hua"
        }
    },
    methods:{
        someMethod(msg){
            console.log(msg)
        }
    }
}

// Parent.vue
<template>
    <child ref="child"></child>
</template>
<script>
export default {
    mounted(){
        const child = this.$refs.child
        console.log(child.name) // Mu Hua
        child.someMethod("The method of the subcomponent was called")
    }
}
</script>

5. $emit / v-on

The child component sends data to the parent component by sending events, or triggers operations such as parent component update

// Distributed by Child.vue
export default {
  data(){
      return { msg: "This is the message sent to the parent component" }
  },
  methods: {
      handleClick(){
          this.$emit("sendMsg",this.msg)
      }
  },
}
// Parent.vue response
<template>
    <child v-on:sendMsg="getChildMsg"></child>
    // Or abbreviation
    <child @sendMsg="getChildMsg"></child>
</template>

export default {
    methods:{
        getChildMsg(msg){
            console.log(msg) // This is the message received by the parent component
        }
    }
}

6.attrs/listeners

When multi-layer nested components transfer data, this can be used if they only transfer data without intermediate processing. For example, when a parent component transfers data to a child component

$attrs: contains a collection of non props attributes in the parent scope except class and style. Get all the qualified attribute sets in the parent scope through this.attrs, and then continue to pass them to other components within the child component. You can use v-bind="attrs"

$listeners: contains a collection of listening events except. native in the parent scope. If you want to continue to pass it to other components inside the sub component, you can use v-on = "$linkers"

Use the same way

// Parent.vue
<template>
    <child :name="name" title="1111" ></child>
</template
export default{
    data(){
        return {
            name:"Mu Hua"
        }
    }
}

// Child.vue
<template>
    // Continue to pass to grandchildren
    <sun-child v-bind="$attrs"></sun-child>
</template>
export default{
    props:["name"], // You can receive or not receive here
    mounted(){
        // If props receives name, it is {title:1111}, otherwise it is {Name: "Muhua", title:1111}
        console.log(this.$attrs)
    }
}

7.children/parent

$children: get an array of VueComponent objects containing all sub components (excluding grandchildren), and you can directly get all data and methods in the sub components

$parent: get the VueComponent object of a parent node, which also contains all data and methods in the parent node

// Parent.vue
export default{
    mounted(){
        this.$children[0].someMethod() // Call the method of the first subcomponent
        this.$children[0].name // Gets the property in the first subcomponent
    }
}

// Child.vue
export default{
    mounted(){
        this.$parent.someMethod() // Call the method of the parent component
        this.$parent.name // Get properties in parent component
    }
}

8. provide / inject

provide / inject is dependency injection, which is not recommended to be directly used in application code, but it is commonly used in some plug-ins or component libraries, so I think it's OK to use it, and it's very useful

Provide: allows us to specify the data or methods we want to provide to future generations of components

inject: receive data or methods that you want to add to any descendant component. No matter how deep the component is nested, it can be used directly

It should be noted that the data passed by provide and inject are not responsive, that is, after receiving the data with inject, the data in provide changes, and the data in descendant components will not change, unless the incoming object is a listening object

Therefore, it is recommended to pass some constants or methods

// Parent component
export default{
    // Method 1 cannot get the method in methods
    provide:{
        name:"Mu Hua",
        age: this.data Properties in
    },
    // Method 2 cannot get the attribute in data
    provide(){
        return {
            name:"Mu Hua",
            someMethod:this.someMethod // Methods in methods
        }
    },
    methods:{
        someMethod(){
            console.log("This is the injection method")
        }
    }
}

// Descendant component
export default{
    inject:["name","someMethod"],
    mounted(){
        console.log(this.name)
        this.someMethod()
    }
}

9. EventBus

EventBus is a central event bus, which can be used to complete communication operations for parent-child components, brother components, cross level components, etc

There are three ways to define

// Method 1
// Extract it into a separate JS file Bus.js, and then introduce it where necessary
// Bus.js
import Vue from "vue"
export default new Vue()

// Method 2 mount directly to the global
// main.js
import Vue from "vue"
Vue.prototype.$bus = new Vue()

// Method 3 is injected into the Vue root object
// main.js
import Vue from "vue"
new Vue({
    el:"#app",
    data:{
        Bus: new Vue()
    }
})

Use the following to take the on-demand introduction of method 1 as an example

// Within a component that needs to send custom events to the outside
<template>
    <button @click="handlerClick">Button</button>
</template>
import Bus from "./Bus.js"
export default{
    methods:{
        handlerClick(){
            // Custom event name sendMsg
            Bus.$emit("sendMsg", "This is the data to be sent externally")
        }
    }
}

// Within a component that needs to receive external events
import Bus from "./Bus.js"
export default{
    mounted(){
        // Trigger of listening event
        Bus.$on("sendMsg", data => {
            console.log("This is the data received:", data)
        })
    },
    beforeDestroy(){
        // Cancel listening
        Bus.$off("sendMsg")
    }
}

10. Vuex

Vuex is a state manager that centrally stores and manages the state of all components. This piece of content is too long. If you are not familiar with the basics, you can look at this vuex, and the general usage is as follows

For example, create such a file structure

The contents in index.js are as follows

import Vue from 'vue'
import Vuex from 'vuex'
import getters from './getters'
import actions from './actions'
import mutations from './mutations'
import state from './state'
import user from './modules/user'

Vue.use(Vuex)

const store = new Vuex.Store({
  modules: {
    user
  },
  getters,
  actions,
  mutations,
  state
})
export default store

Then, it is introduced in main.js

import Vue from "vue"
import store from "./store"
new Vue({
    el:"#app",
    store,
    render: h => h(App)
})

Then in the required components

import { mapGetters, mapMutations } from "vuex"
export default{
    computed:{
        // Method 1, and then you can use it through the name of this. Attribute
        ...mapGetters(["introduce getters.js Inside attribute 1","Attribute 2"])
        // Mode II
        ...mapGetters("user", ["user Attribute 1 in module","Attribute 2"])
    },
    methods:{
        // Method 1, and then you can use it through the name of this. Attribute
        ...mapMutations(["introduce mutations.js Method 1 in","Method 2"])
        // Mode II
        ...mapMutations("user",["introduce user Method 1 in module","Method 2"])
    }
}

// Or you can get it this way
this.$store.state.xxx
this.$store.state.user.xxx

11. $root

$root can get the data and methods in App.vue

12. slot

It is to pass the data of the child component to the parent component through the slot, and then plug it back

// Child.vue
<template>
    <div>
        <slot :user="user"></slot>
    </div>
</template>
export default{
    data(){
        return {
            user:{ name:"Mu Hua" }
        }
    }
}

// Parent.vue
<template>
    <div>
        <child v-slot="slotProps">
            {{ slotProps.user.name }}
        </child>
    </div>
</template>

Tags: Vue

Posted on Tue, 23 Nov 2021 10:32:13 -0500 by Yuzi