Vue3组件间通信入门教程:defineProps、defineEmits、defineExpose快速上手

一、为什么需要组件间通信?

在Vue应用中,组件是独立的功能模块。要让它们协同工作,需要:

  • 父组件向子组件传递数据(如用户信息)
  • 子组件向父组件反馈状态(如表单提交)
  • 父组件直接调用子组件方法(如控制弹窗显示)

Vue3通过三个组合式API提供了简洁的解决方案:

二、defineProps:接收父组件的数据

1. 作用

让子组件接收父组件传递的值(类似Vue2的props选项)

2. 快速上手

vue 复制代码
<!-- 子组件 Child.vue -->
<template>
  <h2>欢迎,{{ username }}</h2>
</template>

<script setup>
// 声明接收的props
const props = defineProps({
  username: String // 接收字符串类型的username
})
</script>
vue 复制代码
<!-- 父组件 Parent.vue -->
<template>
  <Child username="张三" /> <!-- 传递数据 -->
</template>

3. 三个关键点

  • 默认值 :使用withDefaults

    vue 复制代码
    const props = withDefaults(defineProps({
      size: { type: Number, default: 16 }
    }), {
      size: 18 // 最终默认值为18
    })
  • 类型校验 :基础类型校验

    vue 复制代码
    defineProps({
      age: { type: Number, required: true } // 必填项
    })
  • 单向数据流:子组件不能直接修改props,需通过事件通知父组件修改

三、defineEmits:向父组件发送消息

1. 作用

子组件触发事件,父组件监听并响应(类似Vue2的$emit

2. 快速上手

vue 复制代码
<!-- 子组件 Child.vue -->
<template>
  <button @click="sendMsg">发送消息</button>
</template>

<script setup>
// 声明事件
const emits = defineEmits(['message'])

function sendMsg() {
  emits('message', '来自子组件的问候') // 触发事件并传递数据
}
</script>
vue 复制代码
<!-- 父组件 Parent.vue -->
<template>
  <Child @message="handleMessage" />
</template>

<script setup>
function handleMessage(msg) {
  alert(msg) // 弹出:来自子组件的问候
}
</script>

3. 两个重要注意事项

  • 事件命名 :建议使用kebab-case(如update-count

  • 参数传递 :可传递多个参数

    vue 复制代码
    emits('event-name', arg1, arg2)

四、defineExpose:暴露子组件能力

1. 作用

允许父组件直接调用子组件的方法或访问属性

2. 快速上手

vue 复制代码
<!-- 子组件 Child.vue -->
<template>
  <div ref="boxRef"></div>
</template>

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

const boxRef = ref(null)

// 暴露方法
function focusInput() {
  boxRef.value.focus()
}

// 显式暴露
defineExpose({
  focusInput
})
</script>
vue 复制代码
<!-- 父组件 Parent.vue -->
<template>
  <Child ref="childRef" />
  <button @click="callChildMethod">调用子组件方法</button>
</template>

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

const childRef = ref()

function callChildMethod() {
  childRef.value.focusInput() // 调用子组件方法
}
</script>

3. 使用场景

  • 控制子组件内部状态(如弹窗显示/隐藏)
  • 直接操作子组件DOM元素
  • 调用子组件内部方法

五、综合示例:购物车组件

1. 父组件(ShoppingCart.vue)

vue 复制代码
<template>
  <ProductItem 
    :product="product" 
    @add-to-cart="handleAdd"
    ref="productRef"
  />
  <button @click="showDetails">显示商品详情</button>
</template>

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

const product = { id: 1, name: 'Vue3教程' }
const productRef = ref()

function handleAdd() {
  console.log('加入购物车')
}

function showDetails() {
  productRef.value.showDetails() // 调用子组件方法
}
</script>

2. 子组件(ProductItem.vue)

vue 复制代码
<template>
  <div>
    <h3>{{ product.name }}</h3>
    <button @click="emits('add-to-cart')">加入购物车</button>
  </div>
</template>

<script setup>
const props = defineProps(['product'])
const emits = defineEmits(['add-to-cart'])

// 暴露方法
function showDetails() {
  alert('显示商品详情')
}

defineExpose({
  showDetails
})
</script>

六、总结:三步掌握组件通信

  1. 数据传递 :父组件用属性传递,子组件用defineProps接收
  2. 事件通信 :子组件用defineEmits触发,父组件用@监听
  3. 方法调用 :子组件用defineExpose暴露,父组件用ref调用

七、练习建议

  1. 创建一个父组件和两个子组件,实现:
    • 父组件传递用户名到子组件A
    • 子组件A发送消息到父组件
    • 父组件调用子组件B的方法显示弹窗
  2. 尝试给props添加类型校验和默认值
  3. 实践事件传递多个参数

通过这三个API,你可以完成Vue3中90%的组件通信需求。当项目复杂度增加时,再考虑使用Vuex或Pinia进行状态管理。