vue3中defineProps的使用详解
作者:BillKu
1、数组写法
const props = defineProps(["applyBasicInfo", "operateCommandType"]);
这是一个非常基础的 Vue props 声明方式,适用于简单场景。以下是详细解释:
代码含义
const props = defineProps(["applyBasicInfo", "operateCommandType"]);
- 作用:声明组件接收两个 props(属性)
- 特点:仅指定 prop 名称,不进行任何类型校验
- 等价于:
defineProps({
applyBasicInfo: null, // 无类型检查
operateCommandType: null // 无类型检查
})关键特性分析
1.极简声明
- 只声明 props 的名称(字符串数组)
- 不校验类型
- 没有默认值
- 不强制 required
2.适用场景
- 快速原型开发
- 不需要类型检查的简单组件
- 临时过渡代码
3.实际效果
<!-- 父组件 --> <ChildComponent :applyBasicInfo="任何类型的数据" <!-- 可以是对象、字符串、数字等 --> operateCommandType="任何内容" <!-- 可以是任意类型 --> /> <!-- 子组件 --> <script setup> // 使用时需要自行确保类型正确 console.log(props.applyBasicInfo) // 可能是任意类型 </script>
与其他声明方式的对比
| 特性 | 数组写法 | 对象写法(完整) | TypeScript 类型写法 |
|---|---|---|---|
| 类型校验 | ❌ 无 | ✅ Vue 运行时校验 | ✅ 编译时校验 |
| 默认值 | ❌ 不能设置 | ✅ 通过 default 设置 | ✅ 通过 withDefaults 设置 |
| 必需性控制 | ❌ 默认都是可选 | ✅ 通过 required 控制 | ✅ 通过 ? 符号控制 |
| 类型提示 | ❌ 无 | 🟡 基础类型提示 | ✅ 完整类型提示 |
| 代码安全性 | 🔴 低(可能运行时出错) | 🟡 中(有基础校验) | 🟢 高(编译时拦截错误) |
潜在风险示例
<!-- 父组件传递错误类型 --> <ChildComponent :applyBasicInfo="123" <!-- 传递数字 --> operateCommandType <!-- 未传递 --> /> <!-- 子组件使用时 --> <script setup> // 可能引发运行时错误 props.applyBasicInfo.name // 报错:数字没有 name 属性 props.operateCommandType // 拼写错误也不会警告(正确是 operateCommandType) </script>
为什么要用这种写法?
- 历史兼容:Vue 2 的 props 数组写法在 Vue 3 中仍然可用
- 快速开发:节省写类型校验的时间
- 简单场景:当 props 类型不重要或可以确定时
如何改进?
基础改进(添加类型校验)
const props = defineProps({
applyBasicInfo: {
type: Object,
required: true
},
operateCommandType: {
type: String,
default: "view"
}
});高级改进(TypeScript)
interface ApplyBasicInfo {
id: number;
name: string;
}
const props = defineProps<{
applyBasicInfo: ApplyBasicInfo;
operateCommandType?: string;
}>();最佳实践建议
仅用于临时代码:正式项目建议使用完整声明
配合 JSDoc 使用(如果不用 TypeScript):
/**
* @typedef {Object} ApplyBasicInfo
* @property {number} id
* @property {string} name
*/
const props = defineProps(["applyBasicInfo", "operateCommandType"]);渐进式增强:后续逐步替换为完整声明
这种写法就像骑自行车不戴头盔——简单快捷,但不够安全。建议新手仅在快速验证想法时使用,正式开发中尽量使用更完善的 props 声明方式。
2、对象写法(纯 JavaScript 项目的写法,运行时 props 声明)
const props = defineProps({
applyBasicInfo: {
type: Object, // 要求必须是 Object 类型
required: true // 表示父组件必须传递这个prop,不能与 default 同时使用
},
operateCommandType: {
type: String, // 要求必须是 String 类型
default: "info-view" // 表示当父组件没有传递时使用默认值,不能与 required 同时使用
}
});数组简写 vs 对象写法
// 简写方式(不推荐)
defineProps(['applyBasicInfo', 'operateCommandType'])
// vs
// 当前对象写法(推荐)
defineProps({
applyBasicInfo: { type: Object, required: true },
operateCommandType: { type: String, default: "info-view" }
})| 特性 | 数组写法 | 对象写法 |
|---|---|---|
| 类型检查 | ❌ 无 | ✅ 基础类型检查 |
| 默认值 | ❌ 不可设置 | ✅ 可设置 |
| 必填控制 | ❌ 默认全部可选 | ✅ 精确控制 |
| 代码安全性 | 🔴 低 | 🟡 中等 |
这段代码使用 Vue 的 defineProps 方法声明组件需要接收的两个 props(组件参数),并设置了参数的基本验证规则。
3、TypeScript 的类型声明写法
3.1、使用标准的PropType进行对象类型定义
import type { PropType } from "vue";
import type { ApplyBasicInfo } from "@/interface";
const props = defineProps({
applyBasicInfo: {
type: Object as PropType<ApplyBasicInfo>, // 使用标准的PropType进行对象类型定义
required: true // 表示父组件必须传递这个prop,不能与 default 同时使用
},
operateCommandType: {
type: String,
default: "info-view" // 表示当父组件没有传递时使用默认值,不能与 required 同时使用
}
});关键概念拆解
1. 类型导入部分
import type { PropType } from "vue";
import type { ApplyBasicInfo } from "@/interface";PropType:Vue 提供的类型工具,用于在运行时声明中指定复杂类型ApplyBasicInfo:自定义的业务数据类型(通常定义在src/interface.ts等位置)import type:TypeScript 语法,表示只导入类型信息,不导入实际代码
2. 核心 props 声明
applyBasicInfo: {
type: Object as PropType<ApplyBasicInfo>, // 类型断言
required: true
}Object:Vue 的运行时类型校验(基础对象类型)as PropType<ApplyBasicInfo>:类型断言,告诉 TypeScript 这个对象的具体结构
双重效果:
- 运行时:Vue 检查传入的是否为对象
- 开发时:TypeScript 检查对象是否符合
ApplyBasicInfo的结构
3. 对比普通对象声明
// 普通写法(只有 Vue 校验)
applyBasicInfo: {
type: Object, // 只知道是个对象,不清楚具体结构
required: true
}
// 当前写法(Vue校验 + TS类型)
applyBasicInfo: {
type: Object as PropType<ApplyBasicInfo>, // 明确对象结构
required: true
}工作原理图示
graph TD
A[父组件传递 props] --> B{Vue 运行时校验}
B -->|通过| C[组件内使用]
B -->|不通过| D[控制台警告]
C --> E[TypeScript 类型检查]
E -->|类型正确| F[正常开发]
E -->|类型错误| G[IDE 提示错误]为什么要这样写?
优势对比表
| 特性 | 普通 Object 类型 | 当前写法 |
|---|---|---|
| 代码提示 | 只知道是对象,无具体属性提示 | ✅ 显示 ApplyBasicInfo 的所有属性 |
| 重构安全性 | 修改属性时不会报错 | ✅ 修改接口定义会引发相关代码报错 |
| 参数校验 | 只校验是否是对象 | ✅ 运行时对象校验 + 类型结构校验 |
| 协作效率 | 需要查文档看数据结构 | ✅ 直接通过提示查看数据结构 |
示例类型定义
// 假设在 @/interface 中
interface ApplyBasicInfo {
id: number; // 申请ID
applicant: string; // 申请人姓名
status: 'pending' | 'approved'; // 申请状态
createTime: Date; // 创建时间
}在组件中的实际使用
智能提示示例
// 正确使用(有提示) props.applyBasicInfo.id // 提示 number 类型 props.applyBasicInfo.applicant // 提示 string 类型 // 错误使用(立即报错) props.applyBasicInfo.APPlicant // ❌ 拼写错误提示 props.applyBasicInfo.status = 'rejected' // ❌ 类型不匹配
对应模板使用
<template>
<!-- 显示申请人信息 -->
<div>{{ props.applyBasicInfo.applicant }}</div>
<!-- 根据操作类型显示不同内容 -->
<div v-if="operateCommandType === 'info-view'">
查看模式
</div>
</template>常见错误及解决方案
错误1:未定义接口
// 错误提示:Cannot find name 'ApplyBasicInfo'
// 解决方案:确保类型文件正确定义并导出
// 在 @/interface.ts 中:
export interface ApplyBasicInfo { ... }错误2:错误类型传递
<!-- 父组件传递错误数据结构 -->
<MyComponent :apply-basic-info="{ id: '100' }" />
<!-- 错误:id 应该是 number -->错误3:缺少必要属性
// 组件内使用时
const submit = () => {
console.log(props.applyBasicInfo.createTime) // 如果接口中有该属性
}
// 当父组件传递的对象缺少 createTime 时,TS 会报错不同技术栈的写法对比
| 场景 | JavaScript 写法 | TypeScript 基础写法 | 当前进阶写法 |
|---|---|---|---|
| props 类型 | type: Object | type: Object | Object as PropType<T> |
| 代码提示 | 无 | 显示 Object 类型 | 显示具体接口属性 |
| 类型安全 | 低 | 中 | 高 |
| 维护成本 | 高(需口头沟通数据结构) | 中(需查看接口定义) | 低(直接提示数据结构) |
最佳实践建议
接口定义规范:
- 在
src/types或src/interface目录统一管理类型 - 为每个接口添加文档注释
interface ApplyBasicInfo {
/**
* 申请的唯一标识
* @example 123456
*/
id: number;
}props 校验增强:
applyBasicInfo: {
type: Object as PropType<ApplyBasicInfo>,
required: true,
validator: (value: ApplyBasicInfo) => {
return !!value.id // 添加自定义校验逻辑
}
}默认值处理技巧:
// 对于复杂对象,建议提供默认值函数
applyBasicInfo: {
type: Object as PropType<ApplyBasicInfo>,
default: () => ({ id: 0, applicant: '', status: 'pending' })
}这种写法是 Vue 3 项目中平衡类型安全和开发效率的典型实践,既能享受 TypeScript 的类型提示优势,又保留了 Vue 原有的 props 校验机制,特别适合中大型项目维护使用。
3.2、混合写法(结合了 Vue 的运行时 props 校验和 TypeScript 类型提示的混合写法)
import type { ApplyBasicInfo } from "@/interface";
const props = defineProps({
applyBasicInfo: {
type: Object as () => ApplyBasicInfo, // 使用箭头函数返回类型
required: true // 表示父组件必须传递这个prop,不能与 default 同时使用
},
operateCommandType: {
type: String,
default: "info-view" // 表示当父组件没有传递时使用默认值,不能与 required 同时使用
}
});这是一个结合了 Vue 的运行时 props 校验和 TypeScript 类型提示的混合写法代码,特别适用于 Vue 3 + TypeScript 项目。以下是逐层解析:
代码结构解析
import type { ApplyBasicInfo } from "@/interface"; // 导入类型
const props = defineProps({
applyBasicInfo: {
type: Object as () => ApplyBasicInfo, // 核心类型声明
required: true
},
operateCommandType: {
type: String,
default: "info-view"
}
});关键部分详解
1. 类型导入
import type { ApplyBasicInfo } from "@/interface";作用:导入自定义类型(通常定义在 src/interface.ts 或类型声明文件中)
示例类型定义:
// 假设在 @/interface 中
interface ApplyBasicInfo {
id: number;
name: string;
status: 'pending' | 'approved';
createTime: Date;
}2. defineProps 函数
const props = defineProps({...});- Vue 3 特有语法:用于声明组件接收的 props
- 返回响应式对象:包含所有已声明的 props
3. applyBasicInfo 属性
{
type: Object as () => ApplyBasicInfo,
required: true
}- 运行时类型:
Object表示接受对象类型(Vue 的运行时校验) - 类型断言:
as () => ApplyBasicInfo为 TypeScript 提供类型提示 - 实际效果:
// 使用时可以获得类型提示 props.applyBasicInfo.id // 提示 number 类型 props.applyBasicInfo.name // 提示 string 类型
4. operateCommandType 属性
{
type: String,
default: "info-view"
}- 标准 Vue 校验:接受字符串类型
- 默认值:当父组件未传递时默认为 "info-view"
代码特点
| 特性 | 说明 |
|---|---|
| 双重校验 | Vue 运行时类型检查 + TypeScript 静态类型提示 |
| 明确契约 | 通过 ApplyBasicInfo 接口明确定义数据结构 |
| 渐进式类型 | 不需要完全迁移到 TypeScript 也能获得部分类型优势 |
| 代码可维护性 | 接口修改只需改动一处定义 |
不同写法的对比
| 这种写法 | 纯 TypeScript 写法 | 纯 JavaScript 写法 | |
|---|---|---|---|
| 类型提示 | ✅ 完整类型提示 | ✅ 完整类型提示 | ❌ 无 |
| 运行时校验 | ✅ Vue 会校验 Object 类型 | ❌ 需额外配置 | ✅ 标准校验 |
| 默认值支持 | ✅ 直接通过 default 设置 | ❌ 需要使用 withDefaults | ✅ 原生支持 |
| 代码复杂度 | 🟡 中等 | 🟢 简洁(类型优先) | 🔴 高(需自行维护类型约束) |
为什么这样写?
平衡开发体验:
- 获得 TypeScript 的智能提示
- 保留 Vue 的运行时校验安全网
渐进增强:
- 适合逐步将 JavaScript 项目迁移到 TypeScript
- 不需要一次性重写所有类型
明确数据契约:
// 父组件使用时
<MyComponent
:apply-basic-info="{
id: 123, // ✅ 必须 number
name: '张三', // ✅ 必须 string
status: 'pending', // ✅ 必须是枚举值
createTime: new Date() // ✅ 必须 Date
}"
/>常见错误示例
// 错误1:缺少必填字段 props.applyBasicInfo.missingProp // ❌ TS 报错:属性不存在 // 错误2:类型不匹配 props.applyBasicInfo.id = "100" // ❌ TS 报错:不能将 string 赋给 number // 错误3:错误的值类型 <MyComponent :apply-basic-info="'字符串'" /> // ❌ Vue 控制台警告:期望 Object,得到 String
在组件中的使用
<script setup>
// 获得智能提示
const submit = () => {
console.log(props.applyBasicInfo.id) // 正确:number 类型
console.log(props.operateCommandType.toUpperCase()) // 正确:string 方法
}
// 类型安全操作
const statusLabel = computed(() => {
switch(props.applyBasicInfo.status) {
case 'pending': return '审批中';
case 'approved': return '已通过';
// case 'rejected' 会触发 TS 错误(如果接口中未定义)
}
})
</script>最佳实践建议
保持接口同步:当 ApplyBasicInfo 类型修改时,需要同步更新所有使用它的组件
默认值处理:对于复杂对象 prop,建议:
// 使用解构默认值
const { applyBasicInfo = DEFAULT_INFO } = toRefs(props)文档注释:在接口定义处添加详细注释
interface ApplyBasicInfo {
/** 申请ID,唯一标识 */
id: number;
/** 申请人姓名 */
name: string;
// ...
}这种写法特别适合需要逐步引入 TypeScript 的 Vue 项目,既能享受类型系统的优势,又不会完全颠覆原有的开发模式。
3.3、与3.1等效的TypeScript 的类型声明写法
import { toRefs } from "vue";
import type { ApplyBasicInfo } from "@/interface";
const props = defineProps<{
applyBasicInfo: ApplyBasicInfo;
operateCommandType: string;
}>();
这段代码是 Vue 3 组合式 API 中使用 TypeScript 的类型声明写法,主要用于定义组件的 props。让我们分步详细解释:
defineProps 的作用:
- 这是 Vue 3 的组合式 API 函数,用于声明组件接收的 props
- 类似于选项式 API 中的
props选项,但更符合组合式 API 的编程风格
泛型参数 <...>:
- 这是 TypeScript 的泛型语法,用于为 defineProps 提供类型注解
- 泛型参数中定义了一个对象类型,描述了这个组件接收的所有 props 及其类型
props 具体定义:
{
applyBasicInfo: ApplyBasicInfo;
operateCommandType: string;
}a) applyBasicInfo:
b) operateCommandType:
- 声明了组件需要接收两个 props:
- 类型为
ApplyBasicInfo(这应该是一个自定义的 TypeScript 类型/接口) - 可能是一个包含申请基本信息的对象,比如:
interface ApplyBasicInfo {
id: number;
applicantName: string;
applyDate: Date;
// ...其他字段
}- 类型为
string - 可能用于表示操作命令类型,比如:"create"、"edit"、"view" 等字符串枚举值
最终结果:
{
applyBasicInfo: { /* 具体数据 */ },
operateCommandType: "edit" // 示例值
}- 定义了一个名为
props的常量 - 该常量包含两个响应式属性:
在组件中的使用:
// 父组件使用 <ChildComponent :applyBasicInfo="myApplyInfo" operateCommandType="edit" /> // 子组件内使用 console.log(props.applyBasicInfo.applicantName) console.log(props.operateCommandType)
类型检查:
- 如果父组件传递错误类型的 prop,TypeScript 会给出编译时错误
- 例如:传递数字类型的 operateCommandType 会报错
对比 JavaScript 版本:
如果是纯 JavaScript,等效写法可能是:
const props = defineProps({
applyBasicInfo: {
type: Object,
required: true
},
operateCommandType: {
type: String,
default: 'view'
}
})注意事项:
- 需要确保
ApplyBasicInfo类型已经在项目中定义(通常是在独立的类型声明文件中) - 这种写法需要 Vue 3.3+ 版本支持
- 如果不需要 TypeScript 类型检查,可以使用运行时声明方式
- 这种写法默认 props 都是 required 的,如果需要可选参数,应该使用
propName?: Type的语法
实例代码
<script setup lang="ts" name="ApplySampleOtherTable"> // 1、数组写法 const props = defineProps(["applySampleData", "applySampleTableScrollTop", "operateCommandType"]); </script>
<script setup lang="ts" name="ApplySampleOtherTable">
// 2、对象写法
const props = defineProps({
applySampleData: {
type: Object, // 要求必须是 Object 类型
required: true // 表示父组件必须传递这个prop,不能与 default 同时使用
},
applySampleTableScrollTop: {
type: Number, // 要求必须是 Number 类型
default: 0 // 表示当父组件没有传递时使用默认值,不能与 required 同时使用
},
operateCommandType: {
type: String, // 要求必须是 String 类型
default: "info-add" // 表示当父组件没有传递时使用默认值,不能与 required 同时使用
}
});
</script><script setup lang="ts" name="ApplySampleOtherTable">
import type { ApplySample } from "@/interface";
import type { PropType } from "vue";
// 3、TypeScript 的类型声明写法1:使用标准的PropType进行对象类型定义
const props = defineProps({
applySampleData: {
type: Object as PropType<ApplySample[]>, // 要求必须是 Object 类型,类型为ApplySample[]
required: true // 表示父组件必须传递这个prop,不能与 default 同时使用
},
applySampleTableScrollTop: {
type: Number, // 要求必须是 Number 类型
default: 0 // 表示当父组件没有传递时使用默认值,不能与 required 同时使用
},
operateCommandType: {
type: String, // 要求必须是 String 类型
default: "info-add" // 表示当父组件没有传递时使用默认值,不能与 required 同时使用
}
});
</script><script setup lang="ts" name="ApplySampleOtherTable">
import type { ApplySample } from "@/interface";
// 4、TypeScript 的类型声明写法2:结合了 Vue 的运行时 props 校验和 TypeScript 类型提示的混合写法
const props = defineProps({
applySampleData: {
type: Object as () => ApplySample[], // 要求必须是 Object 类型,类型为ApplySample[],使用箭头函数返回类型
required: true // 表示父组件必须传递这个prop,不能与 default 同时使用
},
applySampleTableScrollTop: {
type: Number, // 要求必须是 Number 类型
default: 0 // 表示当父组件没有传递时使用默认值,不能与 required 同时使用
},
operateCommandType: {
type: String, // 要求必须是 String 类型
default: "info-add" // 表示当父组件没有传递时使用默认值,不能与 required 同时使用
}
});
</script><script setup lang="ts" name="ApplySampleOtherTable">
import type { ApplySample } from "@/interface";
// 5、TypeScript 的类型声明写法3:vue3的写法,不设置默认值
const props = defineProps<{
applySampleData: ApplySample[];
applySampleTableScrollTop: number;
operateCommandType: string;
}>();
</script><script setup lang="ts" name="ApplySampleOtherTable">
import type { ApplySample } from "@/interface";
// 6、TypeScript 的类型声明写法3:vue3的写法,设置默认值
const props = withDefaults(
// withDefaults 是 Vue3 提供的一个工具函数,专门用于处理类型声明的 props 默认值。
defineProps<{
applySampleData: ApplySample[];
applySampleTableScrollTop: number;
operateCommandType: string;
}>(),
{
// 数组类型,使用工厂函数返回默认值
applySampleData: () => [],
// 数字类型,使用默认值
applySampleTableScrollTop: 0,
// 字符串类型,使用默认值
operateCommandType: "info-add"
}
);
</script>在 Vue 3 的 <script setup> 语法中,当使用 withDefaults 为对象类型的 prop 设置默认值时,必须使用工厂函数返回默认对象(用箭头函数 () => {} ),这是 Vue 设计层面的强制要求,主要目的是避免多个组件实例共享同一个对象引用的问题。
<script setup lang="ts" name="CommonApplyItemTable">
import type { ApplyItem } from "@/interface";
const props = withDefaults(
defineProps<{
// 受理项目列表
applyItemList: ApplyItem[];
// 受理模式,1:样品检相同项目;2:样品检不同项目
dataType: 1 | 2;
// 操作指令类型:新增删除:info-new;修改:info-modify;查看:info-view
operateCommandType: "info-add" | "info-modify" | "info-view";
}>(),
{
applyItemList: () => {
let applyItemList: ApplyItem[] = [];
return applyItemList;
},
dataType: 1,
operateCommandType: "info-view"
}
);
</script><script setup lang="ts" name="CommonApplyBasicInfoForm">
import type { ApplyBasicInfo, ApplyBasicInfoOperateCommandType } from "@/interface";
const props = withDefaults(
defineProps<{
// 受理基础信息
applyBasicInfo: ApplyBasicInfo;
// 操作指令类型
operateCommandType: ApplyBasicInfoOperateCommandType;
}>(),
{
// 对象类型,使用工厂函数返回默认值
applyBasicInfo: () => {
return {
id: 0,
verifyType: null,
verifyTypeName: "",
outerApplyId: "",
acceptType: "",
acceptTypeName: "",
acceptDate: "",
acceptGroupId: null,
acceptGroupName: "",
acceptPerson: "",
acceptPersonName: "",
reportDate: "",
wspjType: 0,
sjdwName: "",
sjdwAddress: "",
sjdwRegion: "",
cydwName: "",
cydwAddress: "",
cyDate: "",
cyPerson: "",
cyType: "",
wsdwName: "",
wsdwAddress: "",
wsPerson: "",
lxPerson: "",
lxPhone: "",
email: "",
payType: "",
createTime: ""
};
},
// 字符串类型,使用默认值
operateCommandType: "info-view"
}
);总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。
