vue - 以deifne开头的 API
在 Vue 3 中,这些以 define 开头的 API 按实现机制、核心用途可清晰分为编译器宏(Compiler Macros)、工具函数(Utility Functions)、组件定义函数三大类。以下在分类基础上,为每个 API 提供功能解析、使用场景、代码示例及注意事项,兼顾深度与实用性。
一、编译器宏(Compiler Macros)
核心特征:
- 仅在
<script setup>中生效,无需手动导入,Vue 编译器自动识别并在编译阶段处理; - 本质是语法糖,编译时被替换为具体的运行时代码;
- 专为简化
<script setup>中的组件声明设计。
1. defineProps()
核心能力
声明组件的属性(Props),接收父组件传递的数据,支持类型校验、必填性和默认值配置。
代码示例
示例 1:运行时声明(非 TS 项目)
<template>
<div>标题:{{ title }},数量:{{ count }}</div>
</template>
<script setup>
// 声明 props 并设置校验规则
const props = defineProps({
title: {
type: String,
required: true,
default: '默认标题'
},
count: {
type: Number,
default: 0,
validator: (value) => value >= 0 // 自定义校验:值必须非负
}
});
</script>
示例 2:TypeScript 类型声明 + 默认值
<template>
<div>标题:{{ title }},数量:{{ count }}</div>
</template>
<script setup lang="ts">
// 定义类型接口
interface Props {
title: string;
count?: number;
}
// 配合 withDefaults 设置默认值
const props = withDefaults(defineProps<Props>(), {
count: 0
});
</script>
关键细节
- props 是只读的响应式对象,直接修改会报警告;
- 解构 props 时需用
toRefs(props)保留响应式。
常用场景
父组件向子组件传递数据,如列表组件接收数据源、按钮组件接收样式类型等。
2. defineEmits()
核心能力
声明组件的自定义事件,用于子组件向父组件传递数据或触发逻辑,支持事件参数校验。
代码示例
示例 1:运行时声明 + 事件校验
<template>
<button @click="handleClick">触发事件</button>
</template>
<script setup>
// 声明事件并添加校验
const emit = defineEmits({
change: (value) => {
if (value.length > 0) return true;
console.error('值不能为空');
return false;
},
delete: (id) => id > 0
});
// 触发事件
const handleClick = () => {
emit('change', '新值');
emit('delete', 1);
};
</script>
示例 2:TypeScript 类型声明
<template>
<input @input="handleInput" />
</template>
<script setup lang="ts">
// 用类型签名声明事件
const emit = defineEmits<{
(e: 'input', value: string): void;
(e: 'blur', id: number): void;
}>();
const handleInput = (e) => {
emit('input', e.target.value);
};
</script>
关键细节
- 触发事件的参数会被包装为
e.detail(兼容原生事件); - 事件校验返回
false时,事件会被阻止触发。
常用场景
子组件通知父组件状态变化,如输入框值变化、列表项删除等。
3. defineModel() 3.4+
核心能力
Vue 3.4+ 新增,简化自定义组件的 v-model 双向绑定,替代传统的 props + emit 写法。
代码示例
示例 1:基础用法(默认 v-model)

父组件
<template>
<div>
<ChildOldComponent v-model="message" /> old: {{ message }}
<ChildNewComponent v-model="message" /> new: {{ message }}
</div>
</template>
<script setup>
import ChildOldComponent from './components/defineModelOld.vue';
import ChildNewComponent from './components/defineModelNew.vue';
import { ref } from 'vue';
const message = ref('');
</script>
2个子组件
3.4 写法:defineModelNew.vue
<script setup>
const model = defineModel() // 默认对应 `modelValue` prop
</script>
<template>
<input v-model="model" />
</template>
3.4 以前的写法: defineModelOld.vue 3.4
<script setup>
const prop = defineProps({
modelValue: {
type: String,
default: ''
}
})
const emit = defineEmits(['update:modelValue'])
function updateValue(event) {
emit('update:modelValue', event.target.value)
}
</script>
<template>
<input type="text" :value="modelValue" @input="updateValue" />
</template>
示例 2:多 v-model + 修饰符处理
子组件
<template>
<input v-model="name" placeholder="姓名" />
<input v-model="age" placeholder="年龄" />
</template>
<script setup>
// 自定义名称的 v-model
const [name, nameModifiers] = defineModel('name', {
set(value) {
// 检查修饰符并处理
if (nameModifiers.capitalize) {
return value.charAt(0).toUpperCase() + value.slice(1);
}
return value
}
})
const age = defineModel('age');
</script>
父组件
<template>
<!--capitalize修饰符-->
<Child v-model:name.capitalize="userName" v-model:age="userAge" /> {{ userName }} - {{ userAge }}
</template>
<script setup>
import { ref } from 'vue';
import Child from './Child.vue';
const username = ref('');
const userAge = ref(0);
</script>
关键细节
- 底层自动声明
modelValueprop 和update:modelValueemit; - 返回的是可写的响应式引用,修改时自动触发 emit。
常用场景
自定义输入框、开关、滑块等需要双向绑定的组件。
4. defineOptions() 3.3+
核心能力
Vue 3.3+ 新增,在 <script setup> 中直接声明组件的元选项(如名称、属性继承等)。
defineOptions 看起来好像没太大作用,因为它确实不影响功能,主要影响开发体验。让我用更直白的方式解释:
代码示例
<template>
<div>{{ msg }}</div>
</template>
<script setup>
import { ref } from 'vue';
// 定义组件选项
defineOptions({
name: 'MyComponent', // 组件名(用于调试、递归组件)
inheritAttrs: false, // 关闭属性继承
emits: ['change']
});
const msg = ref('Hello Vue');
</script>
关键细节
- 可设置
name、inheritAttrs、props等选项; - 不建议在其中写业务逻辑(如 methods、watch),应在 setup 中实现。
常用场景
为组件命名、关闭属性继承、声明递归组件等。
场景1:你肯定遇到过这个问题——调试时组件名字都是 "Anonymous"
<script setup>
defineOptions({
name: 'MyCounter' // 👈 现在 Vue DevTools 里显示为 <MyCounter>
})
const count = ref(0)
</script>

场景2:插件或库需要组件配置
有些第三方库或插件会读取组件选项:
<script setup>
defineOptions({
// 某些 UI 库需要这个来识别组件类型
componentType: 'form-item',
// 某些路由库需要的配置
keepAlive: true
})
</script>
场景3:控制属性继承
<script setup>
// 不想要父组件传下来的属性自动加到根元素上
defineOptions({
inheritAttrs: false // 👈 阻止自动继承
})
// 然后你可以手动决定属性放哪
const attrs = useAttrs()
</script>
<template>
<div class="wrapper">
<!-- 手动控制属性绑定到哪里 -->
<input v-bind="attrs" />
</div>
</template>
案例1:递归组件(必须有 name)
<script setup>
import { computed } from "vue";
defineOptions({
name: "TreeItem", // 👈 必须!否则无法递归调用自己
});
const props = defineProps(["item"]);
// 递归调用自身
const hasChildren = computed(() => props.item.children?.length > 0);
</script>
<template>
<li>
{{ props.item.name }}
<ul v-if="hasChildren" style="margin-left: 20px">
<!-- 这里要递归调用自己 -->
<TreeItem
v-for="child in props.item.children"
:key="child.id"
:item="child"
/>
</ul>
</li>
</template>5. defineExpose()
核心能力
在 <script setup> 中显式暴露组件内部的属性/方法,供父组件通过 ref 访问。
代码示例
子组件
<template>
<div>计数:{{ count }}</div>
</template>
<script setup>
import { ref } from 'vue';
const count = ref(0);
const increment = () => count.value++;
// 暴露内部属性和方法
defineExpose({
count,
increment
});
</script>
父组件
<template>
<Child ref="childRef" />
<button @click="handleClick">调用子组件方法</button>
</template>
<script setup>
import { ref } from 'vue';
import Child from './Child.vue';
const childRef = ref(null);
const handleClick = () => {
childRef.value.increment(); // 调用子组件方法
console.log(childRef.value.count); // 访问子组件属性
};
</script>
关键细节
<script setup>中内部变量默认私有,仅暴露的内容可被父组件访问;- 暴露的响应式数据保持响应式特性。
常用场景
父组件需要直接操作子组件内部状态或方法的场景。
6. defineSlots()
核心能力
Vue 3.3+ 新增,在 TypeScript 中声明插槽的类型,提升插槽的类型推导能力。
代码示例
<template>
<div>
<slot :item="item" />
<slot name="header" />
</div>
</template>
<script setup lang="ts">
// 声明插槽类型
defineSlots<{
default: (props: { item: string }) => void; // 默认插槽的作用域类型
header: () => void; // 具名插槽无参数
}>();
const item = ref('测试内容');
</script>
关键细节
- 仅用于 TypeScript 类型推导,不影响运行时逻辑;
- 让 IDE 能为插槽提供准确的参数提示。
常用场景
TS 项目中开发带作用域插槽的组件(如列表、表格组件)。
二、工具函数(Utility Functions)
核心特征:
- 需手动导入后使用,非编译器自动识别;
- 运行时执行,用于实现特定功能;
- 可在任意地方使用(不限于
<script setup>)。
1. defineCustomElement()
核心能力
将 Vue 组件编译为原生 Web Components(自定义元素),实现跨框架复用。
代码示例
步骤 1:创建 Vue 组件
<!-- MyElement.vue -->
<template>
<div>
<h2>{{ title }}</h2>
<button @click="handleClick">点击</button>
</div>
</template>
<script setup>
const props = defineProps({
title: { type: String, default: '默认标题' }
});
const emit = defineEmits(['my-click']);
const handleClick = () => {
emit('my-click', Date.now());
};
</script>
步骤 2:编译为自定义元素并注册
// main.js
import { defineCustomElement } from 'vue';
import MyElement from './MyElement.vue';
// 转换为自定义元素构造函数
const MyElementCE = defineCustomElement(MyElement);
// 注册为原生自定义元素(名称需含连字符)
customElements.define('my-element', MyElementCE);
步骤 3:在纯 HTML 中使用
<!DOCTYPE html>
<html>
<body>
<my-element title="Hello Web Components"></my-element>
<script src="./dist/my-element.js"></script>
<script>
// 监听自定义事件
document.querySelector('my-element').addEventListener('my-click', (e) => {
console.log('点击事件:', e.detail);
});
</script>
</body>
</html>
关键细节
- 支持大部分 Vue 特性,但不支持跨 Shadow DOM 的组件通信;
- 自定义元素名称必须包含连字符(符合 W3C 规范)。
常用场景
跨框架复用组件(如 React/Angular 项目中使用 Vue 组件)。
2. defineAsyncComponent()
核心能力
定义异步懒加载组件,减少初始打包体积,提升应用加载速度。
代码示例
示例 1:基础用法
<template>
<AsyncComponent />
</template>
<script setup>
import { defineAsyncComponent } from 'vue';
// 异步加载组件
const AsyncComponent = defineAsyncComponent(() => import('./AsyncComponent.vue'));
</script>
示例 2:完整配置(加载状态 + 错误处理)
<template>
<AsyncComponent />
</template>
<script setup>
import { defineAsyncComponent } from 'vue';
const AsyncComponent = defineAsyncComponent({
loader: () => import('./AsyncComponent.vue'),
loadingComponent: () => import('./Loading.vue'), // 加载中组件
errorComponent: () => import('./Error.vue'), // 加载失败组件
delay: 200, // 延迟 200ms 显示加载组件
timeout: 3000, // 超时 3 秒触发错误组件
onError(error, retry, fail, attempts) {
// 重试 3 次
if (attempts < 3) retry();
else fail();
}
});
</script>
示例 3:路由懒加载(结合 Vue Router)
// router/index.js
import { createRouter, createWebHistory } from 'vue-router';
import { defineAsyncComponent } from 'vue';
const router = createRouter({
history: createWebHistory(),
routes: [
{
path: '/async',
component: defineAsyncComponent(() => import('./AsyncPage.vue'))
}
]
});
export default router;
关键细节
- 加载函数需返回 Promise(
import()天然返回 Promise); - 可配合 Suspense 组件更优雅地处理加载状态。
常用场景
路由懒加载、大型组件(弹窗、图表)的按需加载。
三、组件定义函数
核心特征:
- 需手动导入使用;
- 兼具类型推导和组件定义的作用;
- 是 Vue 3 中定义组件的标准方式之一。
defineComponent()
核心能力
定义 Vue 组件,增强 TypeScript 类型推导,支持选项式和组合式 API。
代码示例
示例 1:选项式 API 用法
<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({
name: 'App',
props: {
title: String
},
data() {
return {
count: 0
};
},
methods: {
increment() {
this.count++;
}
}
});
</script>
示例 2:组合式 API 用法(TS)
<script lang="ts">
import { defineComponent, ref } from 'vue';
export default defineComponent(() => {
const count = ref(0);
const increment = () => count.value++;
return {
count,
increment
};
});
</script>
关键细节
- 在 TS 中能自动推导 props、emit 等类型;
- 是根组件定义的首选方式。
常用场景
TS 项目中定义组件、选项式 API 项目中定义组件、创建根组件。
四、分类总结与使用建议
| 分类 | 包含的 API | 核心特征 |
|---|---|---|
| 编译器宏 | defineProps()、defineEmits()、defineModel()、defineOptions()、defineExpose()、defineSlots() | 无需导入、仅在 <script setup> 中生效、编译阶段处理 |
| 工具函数 | defineCustomElement()、defineAsyncComponent() | 需手动导入、运行时执行、实现特定功能 |
| 组件定义函数 | defineComponent | 需手动导入、支持类型推导、定义组件 |
使用建议
- 优先用编译器宏:在
<script setup>中,用defineProps/defineEmits/defineModel简化开发,提升效率; - 工具函数按需用:跨框架复用用
defineCustomElement,性能优化用defineAsyncComponent; - TS 必用 defineComponent:获得完善的类型提示,减少类型错误;
- 关注版本兼容:如
defineModel需 Vue 3.4+,defineOptions需 Vue 3.3+。

浙公网安备 33010602011771号