Bootstrap

vue常用组件通信方式及vue2和vue3写法对比

前言

大家好,我是南木元元,热衷分享有趣实用的文章。今天来分享一下vue中常用的几种组件间的通信方式在vue2和vue3中分别是如何使用的。

父子组件通信

props/$emit

这种方式是父子组件通信最常用的方式。子组件通过 props 属性来接受父组件的数据,父组件在子组件上注册监听事件,子组件通过 emit 触发自定义事件来向父组件发送数据。

vue2写法

  • 父传子
// Parent.vue
<template>
    <child :msg="msg"></child>
</template>
// Child.vue 
export default {
  //props:['msg'],// 写法一
  props:{// 写法二 
      msg:{
          type:String,
          default:'这是默认数据'
      }
  }
}
  • 子传父
// Parent.vue
<template>
    <child @sendMsg="getChildMsg"></child>
</template>

export default {
    methods:{
        getChildMsg(msg){
            console.log(msg) //身体健康
        }
    }
}
// Child.vue 
export default {
  data(){
      return { msg: "身体健康" }
  },
  methods: {
      handleClick(){
          this.$emit("sendMsg",this.msg)//emit触发自定义事件
      }
  },
}

vue3写法

vue3中子组件通过defineProps获取父组件传递的数据,并且不需要引入,可以直接使用

  • 父传子
//Parent.vue
<template>
   <Child info="身体健康" :money="money"></Child>
</template>

<script setup lang="ts">
import Child from "./Child.vue";
import { ref } from "vue";
let money = ref(10000);
</script>
//Child.vue
<template>
   <p>{{ info }}</p>
   <p>{{ money }}</p>
</template>
<script setup lang="ts">
//使用defineProps方法接受父组件传递过来的数据
const props = defineProps(["info", "money"]); //数组写法
// const props = defineProps({  //对象写法
//   info: String,
//   money: Number,
// });
</script>
  • 子传父

利用defineEmits方法返回函数来触发自定义事件,同样不需要引入,可以直接使用

<template>
  <Child @xxx="handler"></Child>
</template>
<script setup lang="ts">
import Child from "./Child.vue";
const handler = (param1: any, param2: any) => {
  console.log(param1, param2); //好好学习,天天向上
};
</script>
<template>
  <button @click="handler">子传父</button>
</template>
<script setup lang="ts">
//利用defineEmits方法返回函数触发自定义事件
let emit = defineEmits(["xxx"]);
const handler = () => {
  emit("xxx", "好好学习", "天天向上");
};
</script>

ref/$parent

通过 ref 和 $parent ,也可以实现父子组件通信。

  • ref 如果在普通的DOM元素上,引用指向的就是该DOM元素;如果在子组件上,引用的指向就是子组件实例,然后父组件就可以通过可以通过实例来访问组件的数据和方法
  • 使用 $parent,可以让子组件访问父组件的实例,访问的是上一级父组件的属性和方法

vue2写法

子传父:通过ref属性

//Parent.vue
<template>
  <child ref="child"></child>
</template>
<script>
  import child from './child.vue'
  export default {
    components: { child },
    mounted () {
      console.log(this.$refs.child.name);  // 小心
      this.$refs.child.sayHello();  // hello
    }
  }
</script>
// Child.vue
export default {
  data () {
    return {
      name: '小心'
    }
  },
  methods: {
    sayHello () {
      console.log('hello')
    }
  }
}

父传子:使用$parent

// Child.vue
export default{
    mounted(){
        this.$parent.sayHello() // 调用父组件的方法
        this.$parent.name // 获取父组件中的属性
    }
}

vue3写法

注意点:vue3中使用 script setup 的组件是默认关闭的,外部不能访问,如果想获取要获取某个组件的数据和方法,需要在该组件中通过defineExpose来指定需要对外暴露的属性或方法。
子传父:通过ref父组件获取子组件的属性或者调用子组件方法

// Parent.vue
<template>
    <child ref="son"></child>
    <button @click="handlerClick">按钮</button>
</template>
<script setup>
    import child from "./child.vue"
    import { ref } from "vue"
    const son = ref(null)
    const handlerClick = () => {
        console.log(son.value.childName) // 1000000000000
    }
</script>
// Child.vue
<script setup>
    // defineExpose指定需要对外暴露的属性或方法,不需要引入
    defineExpose({
        money:1000000000000})
</script>

父传子:通过$parent在子组件中获取父组件的数据和方法

// Parent.vue
<template>
    <Child></Child>
</template>

<script setup lang="ts">
import Child from "./Child.vue";
import { ref } from "vue";
let money = ref(1000000000000);
// defineExpose指定需要对外暴露的属性或方法,不需要引入
defineExpose({
  money
});
</script>
// Child.vue
<template>
  <div class="dau">
    <button @click="handler($parent)">点击获取父组件实例</button>
  </div>
</template>
<script setup lang="ts">
const handler = ($parent) => {
  console.log($parent.money); //1000000000000
};
</script>

隔代组件通信

provide/inject

provide / inject 为依赖注入,该方法用于父子组件、隔代组件之间的通信,即在层数很深的情况下,可以使用这种方法来进行传值。就不用一层一层的传递了。
provide / inject是Vue提供的两个钩子,和data、methods是同级的。并且provide的书写形式和data一样。

  • provide 用来发送数据或方法
  • inject 用来接收数据或方法

vue2写法

// 父组件
export default{
    provide(){
        return {
            msg: this.msg 
        }
    }
}
// 后代组件
export default{
    inject:["msg"]
}

要注意的是 provide 和 inject 传递的数据不是响应式的,也就是说用 inject 接收来数据后,provide 里的数据改变了,后代组件中的数据不会改变。

vue3写法

// Parent.vue
<script setup>
    import { provide } from "vue"
    provide("name", "小心")
</script>
// Child.vue
<script setup>
    import { inject } from "vue"
    const name = inject("name")
    console.log(name) //小心
</script>

兄弟组件通信

eventBus事件总线

eventBus事件总线,本质是通过创建一个空的 Vue 实例来作为消息传递的对象,通过$on监听事件,通过$emit触发事件,适用于父子、隔代、兄弟组件通信。

vue2写法

1.创建事件中心管理组件之间的通信

// main.js
import Vue from "vue"
Vue.prototype.$bus = new Vue()

2.通过emit触发自定义事件

//兄弟组件A 发送数据
<template>
    <button @click="handlerClick">按钮</button>
</template>
export default{
    methods:{
        handlerClick(){
            // 自定义事件名 sendMsg
            this.$bus.$emit("sendMsg", "这是要向外部发送的数据")
        }
    }
}

3.通过on监听事件

//兄弟组件B 接受数据
export default{
    mounted(){
        // 监听事件的触发
        this.$bus.$on("sendMsg", data => {
            console.log("这是接收到的数据:", data)
        })
    },
    beforeDestroy(){
        // 取消监听
        this.$bus.$off("sendMsg")
    }
}

vue3写法

Vue3中移除了eventBus,但可以借助mitt插件来实现代替,原理还是 EventBus。
1.首先需要安装插件

npm install --save mitt

2.创建总线对象

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

3.通过emit方法触发自定义事件,on方法监听事件

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

参考mitt官网
在这里插入图片描述

Vuex/Pinia

使用Vuex或Pinia状态管理器,集中式存储管理所有组件的状态,实现任意组件间的通信。

  • vuex核心概念:state、mutations、actions、getters、modules
  • pinia核心概念:state、actions、getters。没有mutation、modules。

用法可以参考:Pinia官网Vuex官网。这里就不再具体展开。

结语

本文结合vue2和vue3写法对比,总结了vue中组件通信常用的几种方式。

  • 父子组件:props / $emit、 ref / $parent
  • 隔代组件:provide / inject、eventBus、Vuex / Pinia
  • 兄弟组件:eventBus、Vuex / Pinia

🔥如果此文对你有帮助的话,欢迎💗关注、👍点赞、⭐收藏、✍️评论,支持一下博主~

;