<script setup> 是 Vue 3.2 引入的一种编译时语法糖 ,旨在简化 Composition API 的使用。它并不是一个新的功能,而是对原有 <script> 中使用 Composition API 写法的一种语法优化。
简单来说,它让你用更少的代码 、更直观的写法来实现同样的功能,同时在性能上也有显著提升。
1. 核心对比:传统写法 vs <script setup>
❌ 传统写法 (Vue 3.2 之前)
你需要手动导入 API,定义数据/方法,并显式 return 给模板使用。
xml
<script>
import { ref, reactive } from 'vue'
export default {
components: { MyComponent }, // 需手动注册组件
props: ['title'], // 需手动定义 props
setup(props, { emit }) {
const count = ref(0)
const user = reactive({ name: 'Alice' })
function increment() {
count.value++
}
// ⚠️ 必须手动 return,模板才能访问
return {
count,
user,
increment,
title // props 也要 return
}
}
}
</script>
✅ <script setup> 写法
无需 export default,无需 return,顶层变量自动暴露。
xml
<script setup>
import { ref, reactive } from 'vue'
import MyComponent from './MyComponent.vue' // ✅ 自动注册组件
// ✅ 直接定义 props (编译后自动生成)
defineProps(['title'])
// ✅ 直接定义 emits
const emit = defineEmits(['change'])
// 顶层变量自动暴露给模板,无需 return
const count = ref(0)
const user = reactive({ name: 'Alice' })
function increment() {
count.value++
emit('change', count.value)
}
</script>
2. <script setup> 的五大核心好处
1. 代码更简洁(少写样板代码)
- 无需
export default:组件选项直接在标签内定义。 - 无需
return:在<script setup>中声明的所有顶层变量(ref,function,import的组件等)自动暴露给模板使用。这减少了大量的重复代码和出错可能。 - 组件自动注册 :导入的组件(如
import MyComp from ...)可以直接在模板中使用<MyComp />,无需在components选项中注册。
2. 更好的 TypeScript 支持
- 类型推导更精准 :由于不需要通过
return对象来暴露变量,TS 可以直接推断顶层变量的类型,无需复杂的泛型声明。 - Props/Emits 类型化 :配合
defineProps<Type>()和defineEmits<Type>(),可以获得完美的类型提示和校验,而传统写法需要繁琐的withDefaults或接口定义。
3. 更高的运行时性能
- 编译优化 :
<script setup>的组件会被编译为一个匿名函数 ,作为setup()钩子的实现。 - 避免代理开销 :传统写法中,
setup返回的对象会被 Vue 包装成代理(Proxy)以便模板访问。而<script setup>中的绑定是通过闭包直接访问的,省去了创建代理对象的开销,访问速度更快。 - Tree-shaking:未使用的代码更容易被打包工具剔除。
4. 逻辑更清晰
- 消除"割裂感" :在传统写法中,定义的变量和模板中使用的变量之间隔着一个
return块,阅读时需要上下跳转。<script setup>让代码从上到下线性执行,定义即使用。 - 专注于逻辑:开发者可以更专注于业务逻辑本身,而不是 Vue 的样板结构。
5. 原生支持宏(Macros)
提供了一些编译时宏,无需导入即可直接使用:
defineProps: 声明 props。defineEmits: 声明 emits。defineExpose: 显式暴露属性给父组件(默认情况下<script setup>组件实例是关闭的,即父组件无法通过 ref 访问其内部属性,除非使用此宏)。defineOptions: (Vue 3.3+) 声明组件选项(如name,inheritAttrs)。withDefaults: 为defineProps设置默认值。
3. 特殊用法详解
A. 定义 Props 和 Emits
xml
<script setup>
// 接收 props,具有类型推导
const props = defineProps({
msg: String,
count: { type: Number, required: true }
})
// 定义 emits
const emit = defineEmits(['update:count', 'submit'])
function update() {
emit('update:count', props.count + 1)
}
</script>
B. 暴露给父组件 (defineExpose)
默认情况下,父组件通过 ref 获取子组件实例时,无法访问 <script setup> 内部的变量。如果需要暴露,必须显式声明:
xml
<!-- Child.vue -->
<script setup>
import { ref } from 'vue'
const secret = 'hidden'
const publicData = ref(100)
function publicMethod() {
console.log('called')
}
// 只暴露 publicData 和 publicMethod
defineExpose({
publicData,
publicMethod
})
</script>
C. 配合 TypeScript
typescript
<script setup lang="ts">
interface User {
id: number
name: string
}
// 泛型支持
const props = defineProps<{
userId: number
list: User[]
}>()
// 默认值
withDefaults(defineProps<{
msg?: string
labels?: string[]
}>(), {
msg: 'Hello',
labels: () => ['new'] // 对象/数组默认值需用工厂函数
})
</script>
4. 总结:为什么它是"最佳实践"?
| 特性 | 传统<script>+setup() |
<script setup> |
|---|---|---|
| 代码量 | 多 (需 export, return, register) | 极少 (声明即用) |
| 性能 | 正常 (有代理开销) | 更高 (闭包访问,无代理) |
| TS 支持 | 良好 (但需额外类型声明) | 完美 (原生推导) |
| 组件注册 | 手动 | 自动 |
| 推荐度 | ⭐⭐ (兼容旧项目) | ⭐⭐⭐⭐⭐ (新项目首选) |
结论 :
除非你需要维护非常古老的 Vue 3 早期代码,否则在所有新的 Vue 3 项目中,都应该无条件使用 <script setup> 。它是 Vue 团队官方推荐的默认写法,代表了 Vue 未来的发展方向。