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项目中运行,观察不同方式的数据流向和效果差异。

相关推荐
折翼的恶魔20 分钟前
前端学习之CSS
前端·css·学习
java水泥工26 分钟前
基于Echarts+HTML5可视化数据大屏展示-程序员数据可视化大屏展示
前端·echarts·html5
鸡吃丸子39 分钟前
Tailwind CSS 入门指南
前端·css
ObjectX前端实验室1 小时前
LLM的生态与能力边界&一个基本对话的实现
前端·langchain·llm
LFly_ice1 小时前
学习React-16-useContext
前端·学习·react.js
陈陈CHENCHEN1 小时前
使用 Vite 快速创建 React 项目 - SuperMap iClient JavaScript / Leaflet
前端·react.js
用户6883362059701 小时前
Progressive Web App (PWA)
前端
沢田纲吉1 小时前
《LLVM IR 学习手记(二):变量表达式编译器的实现与深入解析》
前端·编程语言·llvm
小徐_23331 小时前
VitePress 博客变身 APP,支持离线访问,只需这一招。
前端·vitepress·pwa