一个全面的Vue 3组件通信演示展示

✍️ 记录

一、Vue2 中组件通信方式

  • props: 实现父子组件、子父组件、甚至兄弟组件通信
  • 自定义事件: 可以实现子父组件通信
  • 全局事件总线$bus: 可以实现任意组件通信
  • pubsub: 发布订阅模式实现任意组件通信
  • vuex: 集中状态管理容器,实现任意组件通信
  • ref: 父组件获取子组件实例 VC,获取子组件的响应式数据及方法
  • slot: 插槽(默认插槽、 具名插槽、 作用域插槽) 实现父子组件通信

二、Vue3 中组件通信方式

1. Props

Props 是 Vue3 中实现父组件向子组件传递数据的主要方式,和 Vue2 的用法类似,但在 Vue3 中对 props 的类型检测和功能扩展更加强大。

基本用法:

复制代码

js

代码解读

复制代码

复制代码
<!-- Parent --> 
<script setup lang="ts">
 import { ref } from 'vue'
 import Child from './components/Child.vue'
 const msg = ref('Hello from Parent')
 const count = ref(0)
 const userInfo = ref({ name: 'Zhang San', age: 18, })
 function increment() { count.value++ } 
</script> 
<template>
 <div class="props-demo">
 <h2>Props 传值示例(readonly)</h2>
 <div class="parent">
   <h3>父组件数据:</h3>
   <p>消息:{{ msg }}</p>
   <p>计数:{{ count }}</p>
   <p>用户:{{ userInfo.name }} - {{ userInfo.age }}岁</p>
   <button class="btn" @click="increment">计数+1</button>
 </div>
 <Child :message="msg" :counter="count" :user="userInfo" />
 </div>
</template>
复制代码

js

复制代码
<!-- Children --> 
<script setup lang="ts">
 interface Props {
   message?: string counter?: number user?: { name: string age: number }
 }
 defineProps <Props>() 
</script>
<template>
 <div class="child">
   <h3>子组件接收到的数据:</h3>
   <p>消息:{{ message }}</p>
   <p>计数:{{ counter }}</p>
   <p>用户:{{ user?.name }} - {{ user?.age }}岁</p>
 </div> 
</template>

Note:

  • props 的核心是单向数据流,父组件的数据可以通过 props 传递给子组件,但子组件不能直接修改这些数据。
  • 如果子组件需要修改数据,通常会通过事件通知父组件,然后由父组件更新数据。
2. 自定义事件

在 Vue 框架中,事件分为两种,一种是原生的 DOM 事件,另一种是自定义事件 原生 DOM 事件可以让用户与网页进行交互,比如 click, change, mouseenter, mouseleave 等 自定义事件可以实现子组件给父组件传递数据。

自定义事件的基本用法:

在 Vue2 中,子组件通过 $emit 触发事件,父组件通过监听这些事件获取数据。在 Vue3 中,依然可以使用 emits 声明触发的自定义事件,

复制代码

js

代码解读

复制代码

复制代码
<!-- Parent --> 
<script setup lang="ts">
 import { ref } from 'vue'
 import Child from './components/Child.vue'
 const count = ref(0)
 const message = ref('')
 function handleIncrement(step: number) { count.value += step }
 function handleSendMsg(msg: string) { message.value = msg } 
</script> 
<template>
 <div class="custom-event-demo">
   <h2>自定义事件示例</h2>
   <div class="parent">
     <h3>父组件数据:</h3>
     <p>计数:{{ count }}</p>
     <p>收到的消息:{{ message }}</p>
   </div>
   <Child @increment="handleIncrement" @send-message="handleSendMsg" />
 </div>
</template>
复制代码

js

代码解读

复制代码

复制代码
<!-- Children --> 
<script setup lang="ts"> 
  const emit = defineEmits<{ increment: [step: number] 
        sendMessage: [msg: string] }>() 
  function sendMsg() { emit('sendMessage', 'Hello from Child') } 
</script> 
<template> 
  <div class="child">
   <h3>子组件:</h3>
   <button class="btn" @click="emit('increment', 1)"> 通知父组件+1 </button>
   <button class="btn" @click="emit('increment', 2)"> 通知父组件+2 </button>
   <button class="btn" @click="sendMsg"> 发送消息 </button>
  </div> 
</template>

Note:

  • 在 Vue2 中,组件上的所有事件,无论是原生 DOM 事件还是子组件触发的自定义事件,都会被自动绑定到组件实例上。这意味着即使是常见的 @click 事件,在 Vue2 中也是通过自定义事件实现的。
复制代码

js

代码解读

复制代码

复制代码
<ChildComponent @click="handleClick" />
  • 在 Vue3 中,事件的行为更加清晰和规范化:

    • 原生 DOM 事件:直接绑定到组件的根元素上,例如 @click 默认会绑定为 DOM 的 click 事件。

    • 自定义事件:需要声明

      js

      <ChildComponent @click="handleClick" />

这里的 @click 默认绑定为子组件根元素的 DOM 事件,而不是子组件的自定义事件。

3. 全局事件总线

全局事件总线可以实现任意组件通信,在 vue2 中可以根据 VMVC 关系推出全局事件总线。 但是在 vue3 中没有 Vue 钩子函数,也就没有 Vue.prototype,同时组合式 API 写法没有 this,如果想在 Vue3 中使用全局事件总线可以使用 mitt 插件实现。

全局事件总线是 Vue2 中实现任意组件间通信的一种常用方式。通过将一个 Vue 实例作为事件中心,可以在组件之间进行事件的发布和订阅,从而实现解耦的通信模式。

在 Vue2 中,全局事件总线通常通过 Vue.prototype 实现,将一个 Vue 实例挂载到原型上:

复制代码
复制代码
js

// main.js Vue.prototype.$bus = new Vue()

在 Vue3 中使用 mitt实现全局实现总线

复制代码
复制代码
js

// src/utils/eventBus.js import mitt from 'mitt' export const eventBus = mitt()

Vue2 和 Vue3 使用对比

  • 事件发布:

    js

    // vue2 this.bus.emit('custom-event', payload)
    // vue3 import { eventBus } from '@/utils/eventBus'
    eventBus.emit('custom-event', { key: 'value' })

  • 事件订阅:

    js

    // vue2
    this.bus.on('custom-event', (payload) => { console.log('收到数据:', payload) })

    // vue3
    import { eventBus } from '@/utils/eventBus'
    eventBus.on('custom-event', (payload) => { console.log('收到数据:', payload) })

  • 事件销毁:

    js

    // vue2
    this.bus.off('custom-event')

    // vue3
    import { eventBus } from '@/utils/eventBus'
    eventBus.off('custom-event')

4. v-model

v-model 是 Vue 中一种非常常见的双向数据绑定方式,主要用于父子组件之间的通信。在 Vue2 中,v-model 本质上是一个语法糖,绑定了一个 value 属性并监听 input 事件。 在 Vue3 中,v-model 进行了重大改进,支持多个绑定和自定义绑定名称。

Vue2 中的 v-model

在 Vue2 中,v-model 等价于:

复制代码
js


<!-- 父组件 --> 
<ChildComponent v-model="message" /> 
<!-- 等价于 --> 
<ChildComponent :value="message" @input="message = $event" />

Vue3 中的 v-model 改进:

  • 在 Vue3 中,v-model 不再默认绑定 value 和监听 input,而是需要显式声明绑定的 prop 和 event 名称:

    js

  • 支持多个 v-model: Vue3 支持通过自定义名称同时绑定多个 v-model:

    js