最近在回顾 Vue3的核心知识点,今天重点梳理了Props(父传子) 和Emits(子传父) 的使用方式,结合实操代码做了总结,既方便自己后续复习,也希望能帮到同样在学 Vue3 的你们~
一、Props:父组件向子组件传递数据
1.1 核心知识点
Props 是 Vue3 中父组件向子组件传递数据的核心方式,遵循单向数据流原则(子组件只能读取 Props 数据,不能直接修改,修改需通过父组件更新)。结合 TypeScript 可以给 Props 做严格的类型约束,让代码更健壮。(基础概念部分可以参考一下我的上一篇文章)
1.2 实操代码解析
TypeScript
<script setup lang="ts">
import { computed } from 'vue';
// 1. 用TS接口定义Props的类型约束
interface Props{
name:string, // 必传字符串类型
age?:number, // 可选 数字类型
isMall?:boolean, // 可选 布尔类型
user?:object, // 可选 对象类型
status?:'success'|'error'|'warning' // 可选联合类型
}
// 2. 定义Props并设置默认值
const props =withDefaults(defineProps<Props>(),{age:18})
// 没有默认值可以直接const props =defineProps<Props>()
// 知识点:withDefaults用于给可选Props设置默认值,这里给age默认18
// 单向数据流:子组件只能读props.age,不能直接改,改的话要让父组件传新值
</script>
<template>
<div>
<br>
子组件2
{{ props }} <!-- 直接渲染所有props数据 -->
age:{{ props.age }} <!-- 渲染单个props值 -->
</div>
</template>
关键总结
- 用 TS 接口**
interface**可以清晰定义 Props 的类型(必传 / 可选、基础类型 / 联合类型); withDefaults()专门用于给可选 Props 设置默认值(注意:只有可选属性才能设默认值);- 单向数据流:子组件修改 Props 不会同步到父组件,避免数据混乱,这是 Vue 组件通信的核心原则。
二、Emits:子组件向父组件传递事件
子组件想给父组件传数据,不能直接修改父组件数据,而是通过触发自定义事件 的方式(Emits),父组件监听该事件并处理数据。Vue3 中 Emits 有两种写法,结合 TS 推荐第二种~
2.1 基础写法--->字符串数组形式
适合简单场景,直接声明自定义事件名称:
TypeScript
<script setup lang="ts">
// 1. 定义自定义事件(字符串数组形式)
const emits = defineEmits(['sub'])
// 2. 事件处理函数:监听输入框回车,获取值并触发自定义事件
const handler = (e:Event) =>{
// 类型断言:把e.target转为HTMLInputElement,才能拿到value
const val = (e.target as HTMLInputElement).value
// 触发自定义事件,把输入值传给父组件
emits('sub',val)
}
</script>
<template>
<div>
<!-- 监听输入框回车事件,触发handler -->
<input type="text" placeholder="输入" @keyup.enter="handler">
</div>
</template>
2.2 进阶写法--->TS类型字面量写法
TS 项目推荐这种写法,能严格约束事件名称和参数类型,减少错误:
TypeScript
<script setup lang="ts">
// 1. 用TS类型字面量定义Emits:约束事件名、参数类型、返回值
const emits = defineEmits<{
(e:"submit",val:string):void // e=事件名,val=参数(字符串类型),void=无返回值
}>()
// 2. 回车事件处理:触发submit事件,传递输入值
function handler(e:Event){
const val = (e.target as HTMLInputElement).value
emits("submit",val)
}
</script>
<template>
子组件1
<input placeholder="go" @keyup.enter="handler">
</template>
2.3 父组件接受事件
不管子组件用哪种 Emits 写法,父组件都通过**@事件名**监听并处理数据:
TypeScript
<script setup lang="ts">
// 导入子组件
import emit2 from "./study_tree/13.ts中emits.vue"
// 父组件的事件处理函数:接收子组件传递的值
function submit(val:string){
console.log('父接受的事件',val);
}
</script>
<template>
<div>
父组件
<!-- 监听子组件的submit事件,触发父组件的submit函数 -->
<emit2 @submit="submit"></emit2>
</div>
</template>
关键总结
- defineEmits 是 Vue3 定义自定义事件的专用 API,替代了 Vue2 的**
emits**选项; - 字符串数组写法简单,但无类型约束;TS 类型字面量写法能约束事件名和参数类型,适合大型项目;
- 类型断言**
(e.target as HTMLInputElement)**是 TS 中获取 DOM 元素属性的常用方式,避免类型报错; - 子组件**
emits(事件名, 参数)** 触发事件,父组件**@事件名="处理函数"** 接收参数,完成子传父。
三、整体学习总结
- Props 和 Emits 是 Vue3 组件通信的基础,遵循 "父传子用 Props,子传父用 Emits" 的原则;
- 结合 TypeScript 能给 Props/Emits 做类型约束,提前规避运行时错误,这也是现代 Vue 项目的标配;
- 单向数据流是核心:Props 只读,子组件不能直接修改,必须通过父组件更新;Emits 是子组件通知父组件修改数据的 "正规途径";
- 两种 Emits 写法各有适用场景:小项目 / 快速开发用字符串数组,中大型 TS 项目用类型字面量。
Tip
加上ts后,从Props 的接口定义、Emits 的 TS 类型约束,虽然多写几行代码,但能避免运行时的类型错误,也能让组件的入参 / 出参更清晰。