大白话 Vue 3 的 script setup 语法与普通
前端小伙伴们,有没有被Vue组件里的"样板代码"搞到想摔键盘?写个组件要export default
、setup
函数、return
变量三件套,注册组件还要在components
里列一遍,模板里用个变量还得先return
出来……
今天咱们就聊聊Vue3的"代码减负神器"——script setup
语法!用它写组件,就像吃了布洛芬,痛点瞬间缓解,代码量直接砍一半!看完这篇,你不仅能上手写,还能和面试官唠明白它的核心优势~
一、普通<script>
的"三大痛点"
先讲个我上周改需求的经历:给客户做商品详情页,要加个PriceInfo
组件显示价格。用普通<script>
写,代码是这样的:
<!-- 普通<script>写法 -->
<script>
import { ref, computed } from 'vue';
import PriceTag from './PriceTag.vue'; // 引入子组件
export default {
components: { PriceTag }, // 注册子组件(必须写!)
props: {
originalPrice: { type: Number, required: true },
discount: { type: Number, default: 0 }
},
setup(props) {
const finalPrice = computed(() => {
return props.originalPrice * (1 - props.discount / 100);
});
// 必须return才能在模板中使用!
return { finalPrice };
}
};
</script>
<template>
<div>
<!-- 用子组件还得确保已注册 -->
<PriceTag :price="finalPrice" />
</div>
</template>
改需求时我发现:
- 重复代码多:每次都要写
export default
、components
注册、setup
函数和return
; - 模板访问麻烦:变量必须
return
才能在模板用,漏了就报错; - 类型声明冗余:用TS时,
props
和emit
的类型要写两次(props
选项+类型断言)。
二、技术原理:script setup的"代码瘦身逻辑"
script setup
是Vue3推出的语法糖,目标是简化组合式API的使用,让组件代码更简洁、更直观。它的核心设计是:自动处理样板代码,让开发者专注业务逻辑。
1. 自动暴露变量和函数
在script setup
中,声明的变量、函数、响应式对象会自动暴露给模板,无需手动return
。就像给变量装了"传送门",模板里直接用!
2. 组件/指令自动注册
通过import
引入的组件、自定义指令,会自动注册,无需在components
或directives
选项中声明。告别"引入-注册-使用"的三步曲,直接引入就能用~
3. 更简洁的props/emit声明
script setup
提供defineProps
和defineEmit
宏(编译器宏,无需导入),让props
和emit
的声明更简洁,且自动类型推导(配合TS更爽)。
4. 生命周期和组合式API直接使用
onMounted
、watch
等生命周期钩子,以及ref
、reactive
等组合式API,无需从setup
函数内调用,直接在script setup
顶层使用。
三、代码示例:从"繁琐"到"丝滑"的对比
示例1:基础变量与模板访问
普通<script>
写法:
<script>
import { ref } from 'vue';
export default {
setup() {
const count = ref(0);
const increment = () => count.value++;
// 必须return!
return { count, increment };
}
};
</script>
<template>
<button @click="increment">点击次数:{{ count }}</button>
</template>
script setup写法:
<script setup>
import { ref } from 'vue';
// 直接声明变量,自动暴露给模板
const count = ref(0);
const increment = () => count.value++;
</script>
<template>
<button @click="increment">点击次数:{{ count }}</button>
</template>
对比:少了export default
、setup
函数和return
,代码量减少40%!
示例2:组件自动注册
普通<script>
写法:
<script>
import PriceTag from './PriceTag.vue'; // 引入组件
export default {
components: { PriceTag }, // 必须注册!
setup() {
return {};
}
};
</script>
<template>
<PriceTag /> <!-- 可用 -->
</template>
script setup写法:
<script setup>
import PriceTag from './PriceTag.vue'; // 引入即注册!
</script>
<template>
<PriceTag /> <!-- 直接用,无需注册 -->
</template>
对比:省去components
注册步骤,代码更清爽~
示例3:props与emit声明(含TS)
普通<script>
+TS写法:
<script lang="ts">
import { defineComponent, PropType } from 'vue';
export default defineComponent({
props: {
// 类型需要同时声明在props选项和类型断言
user: {
type: Object as PropType<{ name: string; age: number }>,
required: true
}
},
emits: {
'user-updated': (newUser: { name: string; age: number }) => true
},
setup(props, { emit }) {
const updateUser = () => {
emit('user-updated', { name: '新名字', age: 18 });
};
return { updateUser };
}
});
</script>
script setup+TS写法:
<script setup lang="ts">
// defineProps和defineEmit是编译器宏,无需导入
const props = defineProps<{
user: { name: string; age: number };
}>();
const emit = defineEmit<{
(e: 'user-updated', newUser: { name: string; age: number }): void;
}>();
const updateUser = () => {
emit('user-updated', { name: '新名字', age: 18 });
};
</script>
对比:
defineProps
直接通过类型声明props
,无需重复写type
和PropType
;defineEmit
通过类型定义事件,自动推导参数类型;- 无需手动
return
,updateUser
自动暴露给模板。
示例4:生命周期与组合式API
普通<script>
写法:
<script>
import { onMounted, ref } from 'vue';
export default {
setup() {
const data = ref(null);
onMounted(() => {
fetch('/api/data').then(res => res.json()).then(d => data.value = d);
});
return { data };
}
};
</script>
script setup写法:
<script setup>
import { onMounted, ref } from 'vue';
const data = ref(null);
// 直接在顶层调用生命周期钩子
onMounted(() => {
fetch('/api/data').then(res => res.json()).then(d => data.value = d);
});
</script>
对比:代码结构更扁平,逻辑更直观~
四、一张表看尽核心差异
对比项 | 普通<script> | <script setup> |
---|---|---|
变量暴露 | 需通过setup 的return 暴露 | 顶层声明的变量自动暴露给模板 |
组件注册 | 需在components 选项中手动注册 | import 后自动注册(无需额外代码) |
props声明 | 需写props 选项(类型冗余) | 用defineProps 宏(类型自动推导) |
emit声明 | 需写emits 选项(类型冗余) | 用defineEmit 宏(类型自动推导) |
生命周期调用 | 需在setup 函数内调用 | 直接在顶层调用(更扁平) |
代码量 | 较多(样板代码多) | 减少约30%-50%(更简洁) |
TS支持 | 需手动类型断言(易出错) | 自动类型推导(更友好) |
组合式API使用 | 需在setup 函数内组织 | 直接顶层使用(逻辑更清晰) |
五、面试题回答方法
正常回答(结构化):
“Vue3的
<script setup>
是为简化组合式API使用而设计的语法糖,核心特性包括:
- 自动暴露:顶层声明的变量、函数自动暴露给模板,无需
return
;- 自动注册:
import
的组件/指令自动注册,无需components
/directives
选项;- 简化props/emit:通过
defineProps
和defineEmit
宏声明,支持类型自动推导;- 更扁平的结构:生命周期钩子和组合式API可直接在顶层调用,无需嵌套在
setup
函数内;- 更好的TS集成:通过类型声明自动推导
props
/emit
类型,减少冗余代码。”
大白话回答(接地气):
“
<script setup>
就像给Vue组件装了‘代码瘦身器’!以前写组件,得先export default
,再在setup
里写逻辑,最后return
变量给模板——麻烦得很。现在用<script setup>
,声明的变量直接‘传’到模板里,引入的组件不用手动注册,props
和emit
用defineProps
/defineEmit
一写,类型还能自动推导。就像以前做饭要洗锅、切菜、炒菜三步,现在直接给你个‘一键炒菜锅’,步骤少了,效率高了,代码还更清爽~”
六、总结:3个核心优势+2个使用建议
3个核心优势:
- 代码更简洁:减少
export default
、components
注册、return
等样板代码; - 开发更高效:自动暴露、自动注册等特性让开发者专注业务逻辑;
- TS更友好:
defineProps
/defineEmit
的类型推导减少冗余声明,降低出错率。
2个使用建议:
- 复杂组件仍可用普通
<script>
:如果需要使用mixins
、extends
等选项式API,或组件逻辑特别复杂(如需要访问this
),普通<script>
更灵活; - 配合
unref
简化模板:如果模板中频繁访问ref
的value
属性(如count.value
),可以用const { count } = toRefs(props)
或const count = computed(() => props.count)
简化(script setup
中ref
会自动解包,模板中直接用count
即可)。
七、扩展思考:4个高频问题解答
问题1:script setup支持所有组合式API吗?
解答:支持!ref
、reactive
、watch
、computed
等组合式API,以及onMounted
、onUpdated
等生命周期钩子,都可以在script setup
顶层直接使用。
问题2:script setup能访问组件实例this
吗?
解答:不能!script setup
是基于组合式API的语法糖,设计上避免使用this
(this
在setup
函数中是undefined
)。需要访问实例属性时,建议用getCurrentInstance
(但不推荐,会增加耦合):
<script setup>
import { getCurrentInstance } from 'vue';
const instance = getCurrentInstance(); // 获取当前组件实例
console.log(instance?.proxy?.$route); // 访问路由(需谨慎使用)
</script>
问题3:script setup如何处理作用域样式?
解答:scoped
样式在script setup
中正常工作,样式仅影响当前组件的模板。如果需要穿透样式(如修改子组件样式),可以用::v-deep
(Vue3推荐用:deep()
):
<style scoped>
:deep(.child-component-class) {
color: red;
}
</style>
问题4:script setup的性能比普通<script>
好吗?
解答:性能无差异!script setup
是编译时的语法糖,最终会被编译为普通的setup
函数,运行时行为完全一致。
结尾:用script setup,写更爽的Vue组件
script setup
不是颠覆,而是进化——它用更少的代码,让Vue组件的逻辑更清晰、开发更高效。无论是新手还是资深开发者,都能快速上手,告别"样板代码"的烦恼~
下次写Vue组件时,不妨试试script setup
,你会爱上这种"代码减负"的感觉!如果这篇文章帮你理清了思路,记得点个收藏,咱们下期,不见不散!