【转载】Vue3 组件通信 9 种方式详解

引言

在 Vue3 项目开发中,组件通信是核心基本功。很多同学只会 props/emit,遇到兄弟组件、深层嵌套组件、全局状态交互就无从下手。本文整理 Vue3 所有 9 种组件通信方式,覆盖父子、跨层级、兄弟、全局等所有场景,案例可直接运行代码、适用场景、优缺点、避坑指南,一文彻底吃透 Vue3 组件传值。

Vue3 组件通信总览(9种全覆盖)

image

父子组件通信(最常用 4 种)

1. defineProps 父传子(单向下传)

场景:父组件向子组件传递展示数据、配置参数

核心规则:单向数据流,子组件禁止直接修改 props

案例:
父组件

<template>
  <Child :title="title" :list="list" />
</template>

<script setup>
import { ref } from 'vue'
import Child from './Child.vue'
const title = ref('')
const list = ref([1,2,3])
</script>

子组件:

<script setup>
// 企业级规范:类型+默认值+必填
const props = defineProps({
  title: {
    type: String,
    required: true
  },
  list: {
    type: Array,
    default: () => [] // 引用类型必须函数返回
  }
})
</script>
2. defineEmits 子传父(事件上报)

场景:子组件点击、输入、操作后,把数据回传给父组件

案例:
子组件

<template>
  <button @click="handleSend">回传数据给父组件</button>
</template>

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

const handleSend = () => {
  emit('onSubmit', '子组件数据', 666)
}
</script>

父组件

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

<script setup>
const handleSubmit = (str, num) => {
  console.log(str, num)
}
</script>
3. v-model 父子双向绑定

场景:弹窗显隐、输入框、数值双向同步

Vue3 支持 多 v-model,告别 .sync 修饰符

案例:
父组件

<template>
  <Child v-model:visible="dialogVisible" v-model:count="num" />
</template>

<script setup>
import { ref } from 'vue'
const dialogVisible = ref(false)
const num = ref(0)
</script>

子组件

<script setup>
const props = defineProps(['visible', 'count'])
const emit = defineEmits(['update:visible', 'update:count'])

const closeDialog = () => {
  emit('update:visible', false)
}
const addCount = () => {
  emit('update:count', props.count + 1)
}
</script>
4. ref + defineExpose 父调用子方法/取值

场景:父组件主动调用子组件的方法、获取子组件内部数据

setup 默认关闭实例暴露,必须手动 defineExpose

案例:
子组件

<script setup>
import { ref } from 'vue'
const msg = ref('子组件内部数据')
const childFn = () => {
  console.log('子组件方法被调用')
}

// 暴露给父组件
defineExpose({
  msg,
  childFn
})
</script>

父组件

<template>
  <Child ref="childRef" />
</template>

<script setup>
import { ref, onMounted } from 'vue'
import Child from './Child.vue'
const childRef = ref(null)

onMounted(() => {
  // 调用子组件方法、取值
  childRef.value.childFn()
  console.log(childRef.value.msg)
})
</script>

跨层级组件通信(2种)

1. $attrs 自动属性透传

场景:多层嵌套组件,父组件参数无需逐层手动传递

特点:自动透传所有非 props 属性、事件、class、style

案例
父组件(最外层)

<GrandChild title="标题透传" :time="time" />

深层子组件(直接接收)

<template>
  <div>{{ $attrs.title }} {{ $attrs.time }}</div>
</template>
2. provide / inject 祖先向后代全局注入

场景:主题色、全局配置、用户信息、权限、多层嵌套通用数据 学习计算机课

优势:无视层级,任意后代组件均可取值,支持响应式

案例:
祖先组件(提供数据)

<script setup>
import { ref, provide } from 'vue'
const theme = ref('dark')
const userInfo = ref({ name: 'admin', role: 'admin' })

// 注入全局数据
provide('theme', theme)
provide('user', userInfo)
</script>

任意后代组件(接收数据)

<script setup>
import { inject } from 'vue'
const theme = inject('theme')
const user = inject('user')
</script>

注意:传入 ref/reactive 可保持响应式,普通变量无响应式

兄弟组件 / 任意组件通信(2种)

1. Mitt 轻量事件总线(简单场景首选)

场景:兄弟组件、无关联组件简单消息通知、数据交互 学习计算机课

Vue3 移除了原生 bus,推荐使用 mitt

案例:
安装 & 封装 bus.js

npm install mitt
// src/utils/bus.js
import mitt from 'mitt'
export const emitter = mitt()

组件A(发送方)

<script setup>
import { emitter } from '@/utils/bus'
// 触发事件
emitter.emit('refreshList', '我是A组件数据')
</script>

组件B(接收方)

<script setup>
import { emitter } from '@/utils/bus'
import { onMounted, onUnmounted } from 'vue'

// 监听事件
onMounted(() => {
  emitter.on('refreshList', (data) => {
    console.log('接收数据:', data)
  })
})

// 销毁监听(防内存泄漏)
onUnmounted(() => {
  emitter.off('refreshList')
})
</script>
2. Pinia 全局状态管理(复杂项目首选)

场景:全局共享数据、复杂状态、多组件频繁读写、持久化数据

Vue3 官方替代 Vuex ,极简、轻量化、类型友好

案例

定义 store

// src/stores/user.js
import { defineStore } from 'pinia'
export const useUserStore = defineStore('user', {
  state: () => ({
    userName: '',
    token: ''
  }),
  actions: {
    setUser(name) {
      this.userName = name
    }
  }
})

任意组件使用

<script setup>
import { useUserStore } from '@/stores/user'
const userStore = useUserStore()

// 改数据
userStore.setUser('前端开发者')
// 取数据
console.log(userStore.userName)
</script>

极简方案:父组件中转(兄弟通信)

场景:临时少量兄弟交互,不想装插件 、不写状态管理

  • 兄弟A 通过 emit 把数据传给父组件
  • 父组件接收数据、更新自身状态
  • 父组件通过 props 传给兄弟B

缺点:多层嵌套会产生 props 层层穿透,复杂项目不推荐

各通信方式最佳场景选型(实战必看)

  • 普通父子传值:props / emit / v-model
  • 父调用子方法:ref + defineExpose
  • 深层嵌套传全局配置:provide / inject
  • 多层属性透传:$attrs
  • 简单兄弟/跨组件通知:mitt 事件总线
  • 全局共享、复杂状态:Pinia

高频避坑总结

  • 禁止直接修改 props:破坏单向数据流,Vue3 直接报错 学习计算机课
  • mitt 必须手动销毁监听,否则造成内存泄漏
  • provide 普通变量无响应式,必须传 ref/reactive
  • setup 组件默认不暴露实例,必须 defineExpose 父组件才能调用
  • 复杂业务坚决用 Pinia,少用 bus 和多层 props 穿透

总结

Vue3 的 9 种组件通信方式覆盖了所有业务场景:简单父子交互用基础API、跨层用 provide/inject、兄弟简单通信用 mitt、全局状态统一用 Pinia。

转载地址:https://blog.csdn.net/kiritomzzz/article/details/162766682

作者:bingbing-blog

出处:http://www.cnblogs.com/bingbing-blog/

本文版权归作者和博客园共有,转载必须给出原文链接,并保留此段声明,否则保留追究法律责任的权利。

posted @ 2026-10-12 14:14  冰冰的blog  阅读(4)  评论(0)    收藏  举报