核心概念
defineComponent 是 Vue 3 的核心函数,用于定义一个组件的配置对象。它本质上是一个类型增强的工厂函数,将组件选项包裹后返回一个带有类型推导的组件对象。使用 defineComponent 可以让 TypeScript 更准确地推断 props、emit 和组件实例的类型,同时在开发环境提供更好的错误提示。
基础语法
import { defineComponent } from 'vue'
// 基础用法:定义一个没有 props 的组件
export default defineComponent({
template: '<div>基础组件</div>'
})
### 组件选项写法
export default defineComponent({
name: 'MyComponent', // 组件名称(必填)
props: {
title: String // 接收 title prop
},
setup(props) {
// 逻辑代码
return () => <div>{props.title}</div> // 渲染函数
}
})
### 模板写法
export default defineComponent({
name: 'TodoItem',
props: ['todo'], // 接收 todo prop
template: `
<div>
<!-- 使用 prop -->
<p>{{ todo.text }}</p>
</div>
`
})
进阶特性
| 特性 | 描述 | 代码示例 |
|---|---|---|
| 类型推导 | 自动推断 props 类型 | props.title 的类型被推导为 string |
| 命名规范 | 强制要求 name 属性 | 未指定 name 时 TypeScript 会报错 |
| 生命周期 | 支持 setup 函数 | 在 setup 中访问 props 和 emit |
| 组合式 API | 与 ref/reactive 配合 | const count = ref(0) |
### props 类型定义
export default defineComponent({
props: {
// 显式定义类型
items: {
type: Array as PropType<string[]>,
required: true
},
// 带默认值
limit: {
type: Number,
default: 10
}
}
})
### 定义 emit 事件
export default defineComponent({
emits: ['update:count'], // 声明事件
setup(props, { emit }) {
// 触发事件
emit('update:count', 100)
}
})
实战应用
### 通用按钮组件
export default defineComponent({
name: 'BaseButton',
props: {
label: String,
disabled: Boolean
},
template: `
<button :disabled="disabled">
{{ label }}
</button>
`
})
### 计数器组件(组合式 API)
export default defineComponent({
name: 'Counter',
props: {
initialValue: {
type: Number,
default: 0
}
},
setup(props) {
const count = ref(props.initialValue) // 使用 props 默认值
const increment = () => {
count.value++ // 修改状态
}
return () => (
<div>
<p>当前值: {count.value}</p>
<button onClick={increment}>加 1</button>
</div>
)
}
})
### 表单输入组件(带类型校验)
export default defineComponent({
name: 'FormInput',
props: {
modelValue: {
type: String,
required: true
},
placeholder: String
},
setup(props, { emit }) {
const handleChange = (e) => {
emit('update:modelValue', e.target.value) // 自动类型推断
}
return () => (
<input
value={props.modelValue}
onInput={handleChange}
placeholder={props.placeholder}
/>
)
}
})
注意事项
- 必须为组件指定 name 属性,否则 TypeScript 无法推导组件类型
- 不建议直接使用
setup(props)而不定义 props 类型,会导致类型丢失 - 在开发环境移除 defineComponent 会导致组件类型检查失效
- props 验证规则未匹配时不会阻止运行,但会显示控制台警告
常见问题
Q: 为什么组件 props 会变成 any 类型?
A: 未使用 defineComponent 时,Vue 无法进行类型推导,建议始终使用该函数
Q: 如何在 defineComponent 中使用 TypeScript 接口?
A: 通过 defineProps<YourInterface>() 显式定义 props 类型
Q: defineComponent 与 setup 函数的关系?
A: defineComponent 会将 setup 函数包裹并注入 props 和 emit 参数,是组合式 API 的入口
总结
掌握 defineComponent 是写好 Vue 3 组件的关键,它能提升类型安全性并规范组件结构,建议在所有组件定义中使用。