Vue3 组件通信方式的完整版 + Demo 示例代码(保姆级,不完整我倒立喝水)

在这里插入图片描述

简单记忆法:

父子 — props + emit
祖孙 — provide / inject
跨组件 — mitt 或 Pinia
全局状态 — Pinia
多标签页 — BroadcastChannel
iframe/跨域 — postMessage
实时通信 — WebSocket
路由通信 — query/params
父调子方法 — ref + expose

🏢 企业级 Vue3 通信方案架构图:

┌───────────────────────────────────────────────┐
│                (业务模块 / 页面)             │
│                                               │
│   ┌─────────────┐     ┌─────────────┐        │
│   │ 组件 A       │───► │ 组件 B       │        │
│   │(父 ↔ 子)    │◄───│(兄弟组件)   │        │
│   └─────────────┘     └─────────────┘        │
│          │  ▲                   ▲             │
│  props   │  │emit               │ provide     │
│          ▼  │                   │ inject      │
│   ┌─────────────┐     ┌─────────────┐        │
│   │ 子组件 A1    │     │ 子组件 B1    │        │
│   └─────────────┘     └─────────────┘        │
│                                               │
│───────────────────────────────────────────────│
│ 全局状态层                                    │
│                                               │
│     ┌───────────────────────────────────────┐ │
│     │            Pinia(全局状态)           │ │
│     │  用户信息 / 权限 / 菜单 / 配置 / 缓存  │ │
│     └───────────────────────────────────────┘ │
│                    ▲       ▲                  │
│                    │useStore│                 │
│                    │       │bus               │
│───────────────────────────────────────────────│
│ 全局通信事件层                                │
│                                               │
│     ┌───────────────────────────────────────┐ │
│     │          EventBus(mitt)             │ │
│     │ 跨组件无关系通信:消息中心、通知中心   │ │
│     └───────────────────────────────────────┘ │
│                    ▲       ▲                  │
│───────────────────────────────────────────────│
│ 浏览器层组件通信                              │
│                                               │
│   ┌─────────────────────────────────────────┐  │
│   │ BroadcastChannel(多标签页同步)        │  │
│   │ LocalStorage event(跨 Tab 通信)       │  │
│   │ postMessage(iframe / 跨域通信)        │  │
│   └─────────────────────────────────────────┘  │
│───────────────────────────────────────────────│
│ 服务端(实时通信层)                           │
│                                               │
│   ┌─────────────────────────────────────────┐  │
│   │ WebSocket / SSE                         │  │
│   │ 实时消息:告警、监控、聊天、业务推送     │  │
│   └─────────────────────────────────────────┘  │
└───────────────────────────────────────────────┘

🚀 1. 父 → 子:Props

最常用方式,父传子数据。
适用场景:静态/响应式数据下发。

Parent.vue

<template>
  <Child :title="msg" :count="3" />
</template>

<script setup>
import Child from './Child.vue'
const msg = 'Hello Child'
</script>

Child.vue

<template>
  <div>{{ title }} - {{ count }}</div>
</template>

<script setup>
const props = defineProps<{
  title: string,
  count: number
}>()
</script>

🚀 2. 子 → 父:emit 事件

子组件向父组件发送事件。
适用场景:点击、输入、状态上报。

Parent.vue

<template>
  <Child @submit="onSubmit" />
</template>

<script setup>
const onSubmit = (data) => {
  console.log('收到子组件数据:', data)
}
</script>

Child.vue

<template>
  <button @click="emit('submit', '子组件传来的数据')">提交</button>
</template>

<script setup>
const emit = defineEmits(['submit'])
</script>

🚀 3. v-model(双向绑定)

本质是 props + emit。
适用场景:双向绑定组件。

Parent.vue

<template>
  <Child v-model="value" />
  <p>父组件值:{{ value }}</p>
</template>

<script setup>
import { ref } from 'vue'
import Child from './Child.vue'

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

Child.vue

<template>
  <input v-model="model" />
</template>

<script setup>
const props = defineProps<{ modelValue: string }>()
const emit = defineEmits(['update:modelValue'])

const model = computed({
  get: () => props.modelValue,
  set: v => emit('update:modelValue', v)
})
</script>

🚀 4. 插槽(slots)

父向子传递模板内容(UI / DOM)。
适用场景:UI 内容自定义。

Parent.vue

<Child>
  <template #header>
    <h3>自定义 Header</h3>
  </template>

  <template #default>
    内容插槽
  </template>
</Child>

Child.vue

<template>
  <header>
    <slot name="header" />
  </header>
  <main>
    <slot />
  </main>
</template>

🚀 5. ref + defineExpose(父调子方法)

父组件调用子组件方法,子组件显式暴露方法给父组件。
适用场景:弹窗打开、表单校验,大量 UI 组件库使用(如 el-dialog、el-form)。

Parent.vue

<template>
  <Child ref="childRef" />
  <button @click="childRef?.open()">打开子组件方法</button>
</template>

<script setup>
import { ref } from 'vue'
import Child from './Child.vue'

const childRef = ref()
</script>

Child.vue

<script setup>
const open = () => {
  console.log('子组件方法被调用')
}

defineExpose({ open })
</script>

<template>
  <div>子组件</div>
</template>

🚀 6. provide / inject(跨层级)

祖先组件向任意后代传值。
适用场景:样式、主题、表单、上下文对象等。

(Vant、Element Plus 广泛使用)

App.vue(祖先)

<script setup>
import { provide } from 'vue'
provide('theme', 'dark')
</script>

<template>
  <Parent />
</template>

Child.vue(任意深度)

<script setup>
import { inject } from 'vue'

const theme = inject('theme')
console.log('注入到的值:', theme)
</script>

<template>{{ theme }}</template>

🚀 7. 兄弟组件通信(父中转)

兄弟借助父组件管理状态(最推荐)。

兄弟 A → emit → 父 → props → 兄弟 B

适用场景:最常规兄弟通信方式。

Parent.vue

<template>
  <A @change="value = $event" />
  <B :value="value" />
</template>

<script setup>
import { ref } from 'vue'
import A from './A.vue'
import B from './B.vue'

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

A.vue

<script setup>
const emit = defineEmits(['change'])
</script>

<template>
  <input @input="emit('change', $event.target.value)" />
</template>

B.vue

<script setup>
const props = defineProps(['value'])
</script>

<template>
  <p>来自兄弟 A 的值:{{ value }}</p>
</template>

🚀 8. EventBus(跨组件,解耦,小项目用得多)

一个全局的事件中心。
适用场景:组件很多 / 层级深 / 不想全局状态管理。

bus.ts

import mitt from 'mitt'
export const bus = mitt()

A.vue

<script setup>
import { bus } from './bus'
bus.emit('send', '来自 A 的数据')
</script>

B.vue

<script setup>
import { bus } from './bus'
bus.on('send', (msg) => {
  console.log('B 收到数据:', msg)
})
</script>

🚀 9. Pinia(全局状态,推荐)

Vue3 官方状态管理工具。
适用场景:中大型项目的全局状态、用户信息、缓存数据。

store/user.ts

import { defineStore } from 'pinia'

export const useUserStore = defineStore('user', {
  state: () => ({ name: '张三' })
})

任意组件

<script setup>
import { useUserStore } from './store/user'
const user = useUserStore()

console.log(user.name)
</script>

🚀 10. LocalStorage / SessionStorage 通信

持久化通信方式。
适用场景:跨页面通信。

A 页面写入

localStorage.setItem('token', 'abc')

B 页面读取

const token = localStorage.getItem('token')

🚀 11. BroadcastChannel(多标签页通信)

const bc = new BroadcastChannel('app')

bc.postMessage('Hello')

bc.onmessage = (e) => {
  console.log('收到:', e.data)
}

🚀 12. window.postMessage(跨 iframe / 页面)

发送:

window.postMessage({ type: 'msg', data: 123 }, '*')

接收:

window.addEventListener('message', (e) => {
  console.log(e.data)
})

🚀 13. URL query 传参(跨路由)

适用:页面跳转传参。

发送:

router.push({
  path: '/detail',
  query: { id: 1 }
})

接收:

const route = useRoute()
console.log(route.query.id)

🚀 14. 路由 params

跳转:

router.push({ name: 'detail', params: { id: 10 } })

接收:

const route = useRoute()
console.log(route.params.id)

🚀 15. WebSocket(实时通信)

组件之间不直接通信,而是依赖后端推送统一数据。

const ws = new WebSocket("ws://xxx")

ws.onmessage = (e) => {
  console.log('收到消息:', e.data)
}

Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐