Vue3 组件通信全攻略:12种方式与实战示例

Vue3 组件通信全攻略:12种方式与实战示例

在Vue3中,组件通信是构建应用的核心技能。本文系统梳理12种通信方式,从基础到进阶,结合真实场景与代码示例,帮助开发者灵活选择最佳方案。


一、父子组件通信

1. Props + Emit(基础单向数据流)

场景 :父组件传递数据给子组件,子组件触发事件通知父组件
示例

javascript 复制代码
// Parent.vue
<template>
  <Child :message="parentMsg" @update="handleUpdate"/>
</template>
<script setup>
import { ref } from 'vue'
import Child from './Child.vue'

const parentMsg = ref('Hello from Parent')
const handleUpdate = (newMsg) => {
  parentMsg.value = newMsg
}
</script>

// Child.vue
<template>
  <div>{{ message }} <button @click="sendMessage">Send</button></div>
</template>
<script setup>
import { defineProps, defineEmits } from 'vue'

const props = defineProps({
  message: String
})
const emit = defineEmits(['update'])

const sendMessage = () => {
  emit('update', 'Hello from Child')
}
</script>

2. $attrs(透传未声明的属性)

场景 :子组件需要接收父组件传递的非Props属性
示例

javascript 复制代码
// Parent.vue
<template>
  <Child :msg="message" :other="extraData" />
</template>
<script setup>
import Child from './Child.vue'

const message = 'Main Message'
const extraData = { id: 123 }
</script>

// Child.vue
<template>
  <div v-bind="$attrs"></div>
</template>
<script setup>
// $attrs自动包含父组件传递的other属性
</script>

3. Ref + DefineExpose(直接调用子组件方法)

场景 :父组件需要直接调用子组件的方法
示例

javascript 复制代码
// Parent.vue
<template>
  <Child ref="childRef" />
  <button @click="callChildMethod">Call Child</button>
</template>
<script setup>
import { ref } from 'vue'
import Child from './Child.vue'

const childRef = ref(null)
const callChildMethod = () => {
  childRef.value.publicMethod()
}
</script>

// Child.vue
<template>
  <div>Child Component</div>
</template>
<script setup>
import { defineExpose } from 'vue'

const publicMethod = () => {
  console.log('Child method called')
}
defineExpose({ publicMethod })
</script>

二、兄弟组件通信

1. Mitt(轻量级事件总线)

场景 :平级组件间需要事件通知
示例

javascript 复制代码
// emitter.js
import mitt from 'mitt'
export const emitter = mitt()

// BrotherA.vue
<script setup>
import { emitter } from './emitter'
emitter.on('notify', (data) => {
  console.log('BrotherA收到消息:', data)
})
</script>

// BrotherB.vue
<script setup>
import { emitter } from './emitter'
setTimeout(() => {
  emitter.emit('notify', { msg: 'Hello BrotherA' })
}, 1000)
</script>

2. 共享父组件状态(通过$parent)

场景 :兄弟组件通过共同的父组件共享数据
示例

javascript 复制代码
// Parent.vue
<template>
  <BrotherA :shared="state" />
  <BrotherB :shared="state" />
</template>
<script setup>
import { reactive } from 'vue'
import BrotherA from './BrotherA.vue'
import BrotherB from './BrotherB.vue'

const state = reactive({ count: 0 })
</script>

// BrotherA.vue
<template>
  <button @click="increment">Increment</button>
</template>
<script setup>
import { defineProps } from 'vue'

const props = defineProps({
  shared: Object
})
function increment() {
  props.shared.count++
}
</script>

三、跨层级组件通信

1. Provide + Inject(祖孙组件数据传递)

场景 :祖先组件向后代组件传递数据,无需逐层传递
示例

javascript 复制代码
// Ancestor.vue
<template>
  <GrandChild />
</template>
<script setup>
import { provide, ref } from 'vue'
import GrandChild from './GrandChild.vue'

const theme = ref('dark')
provide('theme', theme)
</script>

// GrandChild.vue(隔代组件)
<template>
  <div>Theme: {{ theme }}</div>
</template>
<script setup>
import { inject } from 'vue'

const theme = inject('theme')
</script>

四、高级场景通信

1. V-Model 双向绑定(简化父子交互)

场景 :父组件与子组件之间的双向数据同步
示例

javascript 复制代码
// Parent.vue
<template>
  <Child v-model="value" />
</template>
<script setup>
import { ref } from 'vue'
import Child from './Child.vue'

const value = ref('Initial Value')
</script>

// Child.vue
<template>
  <input :value="modelValue" @input="updateValue($event.target.value)" />
</template>
<script setup>
import { defineProps, defineEmits } from 'vue'

const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])

const updateValue = (val) => {
  emit('update:modelValue', val)
}
</script>

2. 依赖注入(函数/对象传递)

场景 :通过插槽传递函数或配置对象
示例

javascript 复制代码
// Parent.vue
<template>
  <Child>
    <template #default="{ config }">
      <div :style="config">{{ config.title }}</div>
    </template>
  </Child>
</template>
<script setup>
import Child from './Child.vue'
</script>

// Child.vue
<template>
  <div>
    <slot :config="config">
      Default Content
    </slot>
  </div>
</template>
<script setup>
import { reactive } from 'vue'

const config = reactive({ title: 'Slot Injected Config' })
</script>

五、其他补充方案

方式 适用场景 示例代码片段
Vuex/Pinia 全局状态管理 store.commit('increment') / useCounterStore().increment()
LocalStorage 持久化数据共享 localStorage.setItem('key', JSON.stringify(data))
Window全局对象 临时跨组件通信(慎用) window.appData = {...}
ES6 Module Import 常量数据共享 import { CONSTANT } from '@/constants'

六、最佳实践建议

  1. 优先选择标准化方案:Props+Emit满足80%场景,复杂场景再用Provide/Mitt/状态管理
  2. 避免过度使用全局状态:仅当数据需要在多组件间共享时使用Vuex/Pinia
  3. 善用$attrs:减少子组件Props定义,实现属性透传
  4. 谨慎操作Ref:仅在必要时直接调用子组件方法,保持组件解耦
  5. 事件命名规范 :使用on[EventName]监听自定义事件,如on:update

七、扩展学习资源

  • Vue3官方文档 - 组件通信
  • Mitt官方文档
  • Pinia状态管理指南
  • Vue3组合式API详解

通过本文,你可以掌握Vue3组件通信的核心方法,并根据实际场景选择最优方案。建议将示例代码复制到Vue3项目中运行,观察不同方式的数据流向和效果差异。

相关推荐
鱼雀AIGC2 分钟前
如何仅用AI开发完整的小程序<6>—让AI对视觉效果进行升级
前端·人工智能·游戏·小程序·aigc·ai编程
duanyuehuan31 分钟前
Vue 组件定义方式的区别
前端·javascript·vue.js
veminhe35 分钟前
HTML5简介
前端·html·html5
洪洪呀35 分钟前
css上下滚动文字
前端·css
搏博1 小时前
基于Vue.js的图书管理系统前端界面设计
前端·javascript·vue.js·前端框架·数据可视化
掘金安东尼2 小时前
前端周刊第419期(2025年6月16日–6月22日)
前端·javascript·面试
bemyrunningdog2 小时前
AntDesignPro前后端权限按钮系统实现
前端
重阳微噪2 小时前
Data Config Admin - 优雅的管理配置文件
前端
Hilaku2 小时前
20MB 的字体文件太大了,我们把 Icon Font 压成了 10KB
前端·javascript·css
fs哆哆2 小时前
在VB.net中,文本插入的几个自定义函数
服务器·前端·javascript·html·.net