Vue 3 核心进阶:掌握 Props 传参及 TypeScript 最佳实践
在 Vue 3 的组件化开发中,props 是实现父子组件解耦、数据流动的核心机制。本文将带你从基础语法到 TypeScript 高级校验,全面掌握 props 的使用。
一、 Props 的基本定义
在 <script setup> 语法糖中,我们使用 defineProps 宏来声明组件接收的属性。
1. 数组模式(不推荐)
仅用于快速原型开发,缺乏类型约束。
const props = defineProps(['name', 'age'])
2. 对象模式
可以指定基础类型、默认值和是否必传。
const props = defineProps({
name: String,
age: {
type: Number,
default: 18,
required: true
}
})
二、 结合 TypeScript 的高级用法
在使用 lang="ts" 时,使用 类型标注 是官方最推荐的做法,它能提供强大的 IDE 自动补全和编译时检查。
1. 纯类型声明
这种方式最简洁,直接定义接口即可。
interface User {
id: string
name: string
}
const props = defineProps<{
title: string
list: User[]
status?: 'active' | 'inactive' // 使用联合类型约束取值范围
}>()
这里的
status表示是可选的。并且值只能是active|inactive
2. 默认值处理:withDefaults
由于 TS 类型声明模式下无法直接写 default,Vue 提供了 withDefaults 宏:
const props = withDefaults(defineProps<{
title?: string
count?: number
}>(), {
title: '默认标题',
count: 0
})
三、 核心原则:单向数据流
这是 Props 使用中最重要的准则。
- 数据是只读的:子组件绝对不能直接修改
props的成员(例如props.title = 'xxx')。 - 为什么? 这样可以防止子组件意外修改父组件的状态,导致数据流向难以追踪。
如果你需要修改这个值,有两种规范做法:
- 本地化: 将其作为
ref的初始值(仅限初始同步)。 - 派生化: 使用
computed进行转换。
在大多数场景下,子组件应该抛出一个事件来通知父组件做出改变。
四、 综合代码案例:Person 组件
通过父组件App.vue向子组件Person.vue传入Person数组进行展示。
1. 定义类型 (src/types/index.ts)
export interface Person {
id: string
name: string
age: number
}
export type Persons = Person[]
2. 子组件 (Person.vue)
<script setup lang="ts">
import { type Persons } from '@/types'
// 1. 声明 Props
const props = withDefaults(defineProps<{
list: Persons
listTitle?: string
}>(), {
listTitle: '成员列表'
})
</script>
<template>
<div class="person-card">
<h4>{{ listTitle }}</h4>
<hr />
<ul>
<li v-for="p in list" :key="p.id">
{{ p.name }} - {{ p.age }} 岁
</li>
</ul>
</div>
</template>
<style scoped>
.person-card {
border: 1px solid #42b883;
padding: 15px;
border-radius: 8px;
background: #f9f9f9;
}
</style>
3. 父组件 (App.vue)
<script setup lang="ts">
import { ref } from 'vue'
import Person from './components/Person.vue'
import { type Persons } from './types'
// 父组件准备响应式数据
const users = ref<Persons>([
{ id: '01', name: '张三', age: 28 },
{ id: '02', name: '李四', age: 32 }
])
</script>
<template>
<div class="app-container">
<h1>Vue 3 Props 演示</h1>
<Person :list="users" listTitle="技术部员工指南" />
</div>
</template>
五、 避坑指南与技巧
1. 解构 Props 会失去响应式
如果你写 const { list } = defineProps(...),那么当父组件更新 list 时,子组件解构出来的变量不会更新。
- 解决方法:使用
toRefs。
import { toRefs } from 'vue'
const props = defineProps<{ count: number }>()
const { count } = toRefs(props) // 此时 count 是响应式的
2. 深度监听与引用类型
当 Props 传递的是对象或数组时,子组件内虽然不能替换整个对象,但由于 JS 引用类型的特性,修改对象内部属性(如 props.list[0].name = 'xxx')虽然能生效,但强烈不建议这样做,因为它违反了单向数据流。
总结:
props 是组件的“输入接口”,通过 TypeScript 加持后的 defineProps 能够让你的组件库具备极高的健壮性。

浙公网安备 33010602011771号