本文是 Vue3 组合式 API 系列的进阶篇,聚焦<script setup> 语法糖的全部用法、实战场景、避坑技巧 ,从基础入门到高级实战,手把手教你吃透 setup 语法糖,大幅提升 Vue3 开发效率。
前置要求 :掌握 Vue3 组合式 API 基础(ref、reactive、computed 等),了解 Vue2 选项式 API 与 Vue3 组合式 API 的区别,具备基础 Vue3 项目开发能力。
一、前言:为什么要使用
在 Vue3 推出组合式 API 初期,我们使用setup() 函数作为组合式逻辑的入口,虽然解决了选项式 API 逻辑碎片化的问题(比如一个业务逻辑的代码分散在 data、methods、computed 中),但在实际开发中存在三个明显痛点,严重影响开发效率:
- 手动暴露冗余 :所有需要在模板中使用的变量、方法,都必须手动通过
return暴露,代码冗余且容易遗漏,一旦遗漏就会导致模板渲染失败,排查起来耗时费力; - 组件注册繁琐 :组件导入后,必须在
components选项中手动注册(如components: { XXX }),哪怕是常用的基础组件,也需要重复注册,增加不必要的代码量; - TS 适配不佳:与 TypeScript 结合时,类型声明繁琐,需要手动定义接口、标注类型,开发体验不够流畅,难以发挥 TS 的类型校验优势。
为了解决这些问题,Vue3.2 版本正式推出 <script setup> 语法糖,它并非新增功能,而是 setup() 函数的"语法糖简化版"------保留了组合式 API 的所有功能,同时大幅简化代码、提升开发效率,目前已成为 Vue3 开发的主流写法,几乎所有 Vue3 项目(包括 Vue3 + TS 项目)都会优先使用。
先看一组直观对比,快速感受语法糖的优势:
1.1 传统 setup() 函数写法
vue
<template>
<div>
<p>{{ count }}</p>
<button @click="increment">+1</button>
<HelloWorld />
</div>
</template>
<script>
import { ref } from 'vue'
import HelloWorld from './HelloWorld.vue'
export default {
// 手动注册组件,哪怕只导入一个也需要写
components: { HelloWorld },
// 手动定义 setup 函数,作为组合式逻辑入口
setup() {
// 定义响应式变量
const count = ref(0)
// 定义方法
const increment = () => count.value++
// 手动 return 暴露,模板才能使用,少写一个就报错
return {
count,
increment
}
}
}
</script>
1.2 <script setup> 语法糖写法
vue
<template>
<div>
<p>{{ count }}</p>
<button @click="increment">+1</button>
<HelloWorld />
</div>
</template>
<!-- 只需在 script 标签上添加 setup 关键字,无需其他多余配置 -->
<script setup>
import { ref } from 'vue'
// 导入组件自动注册,无需手动写 components 选项
import HelloWorld from './HelloWorld.vue'
// 顶层声明的变量、方法,自动暴露给模板,无需 return
const count = ref(0)
const increment = () => count.value++
</script>
对比可见,语法糖写法删除了冗余的export default、components 注册、return 暴露,代码量减少近一半,且逻辑更清晰------所有组合式逻辑都能在顶层直接编写,无需嵌套在 setup 函数中。接下来,我们从 0 到 1 实战掌握它的所有用法,覆盖基础、进阶、实战、避坑全场景。
二、基础入门:<script setup> 核心基础用法
这一部分是语法糖的基础,也是日常开发中最常用的内容,必须熟练掌握。核心原则:<script setup> 内部的代码,本质上就是 setup() 函数的函数体,所有规则与 setup() 函数一致,只是简化了写法。
2.1 基本使用:语法格式与自动暴露
<script setup> 的使用非常简单,只需在 <script> 标签上添加 setup 关键字即可,无需额外配置,核心规则如下(重点记):
- 顶层声明的变量、函数、类 ,会自动暴露给模板使用,无需手动
return;注意:仅顶层声明有效,嵌套在函数内部的变量/方法不会自动暴露。 - 语法糖内部的代码,相当于在
setup()函数内部执行,this指向undefined(刻意设计,避免开发者依赖 this,更符合组合式API"脱离this" 的设计理念)。 - 默认情况下,不能与传统的
<script>标签同时使用(即一个组件中不能有两个 script 标签);特殊场景(如需要配置组件选项)可结合使用,后续进阶部分会讲解。 - 语法糖内部无法直接访问组件的选项式 API(如
data、methods、computed等),因为它本身就是组合式 API 的简化写法,建议全程使用组合式 API 编写逻辑。
实战示例:基础变量与方法
vue
<template>
<div class="base-demo">
<h3>基础用法演示</h3>
<p>姓名:{{ name }}</p>
<p>年龄:{{ age }}</p>
<p>是否成年:{{ isAdult }}</p>
<button @click="changeName">修改姓名</button>
<button @click="changeAge">增长年龄</button>
</div>
</template>
<script setup>
// 1. 基础变量(非响应式,仅演示自动暴露)
// 注意:非响应式变量修改后,模板不会自动更新
const name = '张三'
// 2. 响应式变量(ref 包装基本类型)
import { ref, computed } from 'vue'
const age = ref(20)
// 3. 计算属性(自动暴露,无需 return)
const isAdult = computed(() => age.value >= 18)
// 4. 顶层函数,自动暴露给模板
const changeName = () => {
// 非响应式变量修改,模板不更新
name = '李四' // 无效,模板仍显示"张三"
console.log(name) // 控制台打印"李四",但模板无变化
}
const changeAge = () => {
// 响应式变量修改,需通过 .value 操作
age.value++
// 计算属性会自动响应依赖变化
}
</script>
关键注意点:
- 非响应式变量(如 const name = '张三')修改后,模板不会更新,因为 Vue 无法监听基本类型的直接赋值,需使用 ref 或 reactive 包装为响应式。
- ref 包装的响应式变量,在 script 中修改时需加
.value,模板中使用时无需加.value(Vue 自动解包)。
2.2 组件导入与自动注册
这是 <script setup> 最实用的特性之一:组件导入后自动注册,无需在 components 选项中手动声明,大幅减少冗余代码。
基本用法
vue
<template>
<div>
<!-- 直接使用导入的组件,无需注册 -->
<HelloWorld />
<UserCard :name="name" />
</div>
</template>
<script setup>
// 导入组件,自动注册
import HelloWorld from './HelloWorld.vue'
import UserCard from './UserCard.vue'
// 定义传递给子组件的变量
const name = '张三'
</script>
进阶用法:重命名组件
如果导入的组件名称与当前组件内的变量/函数重名,或想简化组件名称,可使用 ES6 解构重命名:
vue
<template>
<div>
<!-- 使用重命名后的组件名称 -->
<Hello />
<Card :name="name" />
</div>
</template>
<script setup>
// 重命名导入,避免命名冲突
import { default as Hello } from './HelloWorld.vue'
import { default as Card } from './UserCard.vue'
const name = '张三'
</script>
进阶用法:动态导入组件
结合动态导入(懒加载),优化组件加载性能,适用于组件体积较大或按需加载的场景:
vue
<template>
<div>
<!-- 动态组件,按需渲染 -->
<component :is="AsyncComponent" />
<button @click="showComponent = true">显示组件</button>
</div>
</template>
<script setup>
import { ref, defineAsyncComponent } from 'vue'
// 动态导入组件(懒加载),自动注册
const AsyncComponent = defineAsyncComponent(() => import('./AsyncComponent.vue'))
// 控制组件是否显示
const showComponent = ref(false)
</script>
注意:动态导入组件时,需使用 defineAsyncComponent 包裹,这是 Vue3 提供的专门用于动态导入组件的 API,与 setup 语法糖兼容。
2.3 响应式数据:ref、reactive 与解构
setup 语法糖中,响应式数据的使用与 setup() 函数完全一致,核心还是 ref(包装基本类型)和 reactive(包装引用类型),但需注意解构时的响应式丢失问题。
1. ref 用法(推荐用于基本类型)
vue
<script setup>
import { ref } from 'vue'
// 基本类型响应式(number、string、boolean 等)
const count = ref(0)
const name = ref('张三')
const isShow = ref(false)
// 修改响应式数据(必须加 .value)
const increment = () => {
count.value++
}
const changeName = () => {
name.value = '李四'
}
const toggleShow = () => {
isShow.value = !isShow.value
}
</script>
2. reactive 用法(推荐用于引用类型)
vue
<script setup>
import { reactive } from 'vue'
// 引用类型响应式(对象、数组)
const user = reactive({
name: '张三',
age: 20,
address: {
province: '广东',
city: '深圳'
}
})
const list = reactive([1, 2, 3, 4])
// 修改响应式数据(无需 .value,直接修改属性)
const updateUser = () => {
user.age = 21
user.address.city = '广州'
}
const addItem = () => {
list.push(5)
}
</script>
3. 解构响应式数据(避坑重点)
直接解构 reactive 包装的对象,会导致响应式丢失------修改解构后的变量,不会同步更新模板。解决方案:使用 toRefs 或 toRef 解构。
vue
<template>
<div>
<p>姓名:{{ name }}</p>
<p>年龄:{{ age }}</p>
<button @click="changeInfo">修改信息</button>
</div>
</template>
<script setup>
import { reactive, toRefs, toRef } from 'vue'
const user = reactive({
name: '张三',
age: 20
})
// 错误写法:直接解构,响应式丢失
// const { name, age } = user
// 修改 name 不会更新模板
// 正确写法1:使用 toRefs 解构(适用于所有属性)
const { name, age } = toRefs(user)
// 正确写法2:使用 toRef 解构(适用于单个属性)
// const name = toRef(user, 'name')
// const age = toRef(user, 'age')
const changeInfo = () => {
// 解构后需加 .value 修改
name.value = '李四'
age.value = 21
}
</script>
关键总结:
ref包装的变量,无论在script中还是解构后,都需加.value修改。reactive包装的对象,直接修改属性无需.value,但解构时必须用toRefs/toRef,否则丢失响应式。
三、进阶用法:setup 语法糖核心特性
掌握基础用法后,我们来看 setup 语法糖的进阶特性,这些特性能解决更多复杂场景的需求,进一步提升开发效率,也是面试中常考的知识点。
3.1 与传统 script 标签结合使用
默认情况下,一个组件中只能有一个 <script setup> 标签,但在某些场景下(如需要配置组件选项:name、inheritAttrs、props 校验等),可以结合传统的 <script> 标签使用------两个 script 标签共存,各司其职。
核心规则:传统 script 标签用于配置组件选项(export default 导出),setup 语法糖用于编写组合式逻辑,两者互不冲突。
vue
<template>
<div>
<p>{{ name }}</p>
</div>
</template>
<!-- 传统 script 标签:配置组件选项 -->
<script>
export default {
// 配置组件名称(用于调试、递归组件等)
name: 'MyComponent',
// 关闭属性继承(避免 attrs 自动绑定到根元素)
inheritAttrs: false,
// 组件props校验(也可在 setup 中用 defineProps 定义)
props: {
name: {
type: String,
required: true,
default: '默认名称'
}
}
}
</script>
<!-- setup 语法糖:编写组合式逻辑 -->
<script setup>
// 可直接使用传统 script 中定义的 props
import { useAttrs } from 'vue'
// 获取组件 attrs(因 inheritAttrs: false,需手动绑定)
const attrs = useAttrs()
console.log(attrs)
</script>
常见使用场景:
- 需要配置组件 name(用于调试、递归组件、KeepAlive 缓存等)。
- 需要配置 inheritAttrs、components(虽然 setup 可自动注册,但特殊场景可手动配置)。
- 需要编写复杂的 props 校验(虽然 setup 中可通过 defineProps 定义,但传统 props 选项写法更灵活)。
3.2 Props 定义与校验(defineProps)
在 setup 语法糖中,无法直接使用传统的 props 选项,需通过 Vue3 提供的 defineProps 宏函数定义 props,支持 props 校验、默认值等功能,与传统 props 选项完全兼容。
注意:defineProps 是宏函数,无需导入,可直接在 setup 语法糖中使用(Vue 自动注入)。
基础用法:简单 props 定义
vue
<template>
<div>
<p>父组件传递的名称:{{ name }}</p>
<p>父组件传递的年龄:{{ age }}</p>
</div>
</template>
<script setup>
// 基础写法:数组形式(仅定义 props 名称,无校验)
// const props = defineProps(['name', 'age'])
// 推荐写法:对象形式(支持校验、默认值)
const props = defineProps({
name: {
type: String, // 类型校验
required: true, // 是否必传
message: 'name 为必填项,且必须是字符串' // 校验失败提示
},
age: {
type: Number,
required: false,
default: 18, // 默认值
validator: (value) => {
// 自定义校验规则:年龄必须大于 0
return value > 0
}
}
})
// 使用 props(无需 .value,直接使用)
console.log(props.name)
console.log(props.age)
</script>
进阶用法:与 TypeScript 结合(类型推导)
在 Vue3 + TS 项目中,defineProps 支持通过 TypeScript 类型直接定义 props,无需编写繁琐的对象形式,TS 会自动进行类型校验,开发体验更好。
vue
<script setup lang="ts">
// 方式1:直接通过类型定义 props(无默认值)
const props = defineProps<{
name: string
age?: number // 可选属性
gender: 'male' | 'female' // 联合类型
}>()
// 方式2:结合 withDefaults 定义默认值(推荐)
const props = withDefaults(
defineProps<{
name: string
age?: number
gender?: 'male' | 'female'
}>(),
{
age: 18,
gender: 'male'
}
)
// 使用 props
console.log(props.name)
console.log(props.age)
</script>
关键注意点:withDefaults 是 Vue3 提供的宏函数,用于给 TS 类型定义的 props 设置默认值,无需导入,直接使用。
3.3 事件触发:defineEmits
在 setup 语法糖中,子组件向父组件触发事件,需通过 defineEmits 宏函数定义事件,替代传统的 emits 选项,支持事件校验、类型定义等功能。
注意:defineEmits 也是宏函数,无需导入,直接使用。
基础用法:简单事件触发
vue
<!-- 子组件 Child.vue -->
<template>
<button @click="handleClick">触发父组件事件</button>
<button @click="handleSend">触发事件并传参</button>
</template>
<script setup>
// 基础写法:数组形式(仅定义事件名称)
// const emit = defineEmits(['click', 'send'])
// 推荐写法:对象形式(支持事件参数校验)
const emit = defineEmits({
// 无参数事件
click: () => true,
// 有参数事件,校验参数格式
send: (data: string) => {
return typeof data === 'string'
}
})
// 触发事件
const handleClick = () => {
emit('click')
}
// 触发事件并传递参数
const handleSend = () => {
emit('send', '子组件传递的参数')
}
</script>
vue
<!-- 父组件 Parent.vue -->
<template>
<Child @click="handleChildClick" @send="handleChildSend" />
</template>
<script setup>
import Child from './Child.vue'
const handleChildClick = () => {
console.log('子组件触发了 click 事件')
}
const handleChildSend = (data) => {
console.log('子组件传递的参数:', data)
}
</script>
进阶用法:与 TypeScript 结合
vue
<script setup lang="ts">
// 通过 TS 类型定义事件(推荐,类型自动校验)
const emit = defineEmits<{
(e: 'click'): void // 无参数事件
(e: 'send', data: string): void // 有参数事件
}>()
// 触发事件(参数类型错误会报错)
const handleSend = () => {
emit('send', '正确参数') // 正常触发
// emit('send', 123) // TS 报错,参数类型必须是 string
}
</script>
3.4 插槽使用:defineSlots
在 setup 语法糖中,可通过 defineSlots 宏函数定义组件的插槽,支持插槽类型校验(主要用于 Vue3 + TS 项目),替代传统的 slots 选项。
注意:defineSlots 仅用于类型定义和校验,无需手动注册插槽,模板中可直接使用插槽。
vue
<!-- 子组件 Child.vue -->
<template>
<div>
<!-- 默认插槽 -->
<slot>默认内容</slot>
<!-- 具名插槽 -->
<slot name="title">默认标题</slot>
<!-- 作用域插槽 -->
<slot name="item" :data="list"></slot>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const list = ref([1, 2, 3])
// 定义插槽类型(TS 项目推荐,非 TS 项目可省略)
defineSlots<{
// 默认插槽
default?: () => any
// 具名插槽
title?: () => any
// 作用域插槽(指定传递给父组件的参数类型)
item?: (props: { data: number[] }) => any
}>()
</script>
vue
<!-- 父组件 Parent.vue -->
<template>
<Child>
<!-- 默认插槽 -->
<div>父组件默认插槽内容</div>
<!-- 具名插槽 -->
<template #title>
<h2>父组件标题插槽</h2>
</template>
<!-- 作用域插槽 -->
<template #item="slotProps">
<div v-for="item in slotProps.data" :key="item">{{ item }}</div>
</template>
</Child>
</template>
3.5 访问组件实例:useAttrs 与 useSlots
在 setup 语法糖中,this 指向 undefined,无法通过 this 访问组件实例的 attrs、slots 等属性,需通过 useAttrs 和 useSlots 两个 API 访问。
1. useAttrs:访问组件的 attrs
attrs 包含父组件传递的、未被 props 接收的属性(如 class、style、自定义属性等),与传统的 this.$attrs 功能一致。
vue
<template>
<div :class="attrs.class" :style="attrs.style">
{{ attrs.customAttr }}
</div>
</template>
<script setup>
import { useAttrs } from 'vue'
// 获取 attrs 对象
const attrs = useAttrs()
// 访问 attrs 中的属性
console.log(attrs.customAttr)
console.log(attrs.class)
</script>
2. useSlots:访问组件的 slots
slots 包含父组件传递的所有插槽内容,与传统的 this.$slots 功能一致,主要用于在 script 中操作插槽(如判断插槽是否存在)。
vue
<script setup>
import { useSlots } from 'vue'
// 获取 slots 对象
const slots = useSlots()
// 判断默认插槽是否存在
console.log(slots.default) // 存在则返回插槽内容,不存在则返回 undefined
// 判断具名插槽是否存在
console.log(slots.title)
</script>
四、实战场景:setup 语法糖常用组合用法
结合前面的知识点,我们通过几个实战场景,演示 setup 语法糖的常用组合用法,覆盖日常开发中最常见的场景,帮助你快速上手。
4.1 场景1:基础页面开发(变量、方法、组件引入)
vue
<template>
<div class="home-page">
<Header title="首页" />
<div class="content">
<h3>欢迎来到首页,{{ username }}</h3>
<p>当前登录时长:{{ loginTime }} 秒</p>
<button @click="logout">退出登录</button>
</div>
<Footer />
</div>
</template>
<script setup>
// 引入组件(自动注册)
import Header from './components/Header.vue'
import Footer from './components/Footer.vue'
// 响应式变量
import { ref, onMounted } from 'vue'
const username = ref('张三')
const loginTime = ref(0)
// 方法
const logout = () => {
alert('退出登录成功')
// 实际开发中可结合路由跳转
}
// 生命周期钩子(直接使用,无需注册)
onMounted(() => {
// 模拟登录时长统计
setInterval(() => {
loginTime.value++
}, 1000)
})
</script>
<style scoped>
.home-page {
min-height: 100vh;
display: flex;
flex-direction: column;
}
.content {
flex: 1;
padding: 20px;
}
</style>
4.2 场景2:子父组件通信(props + emit)
vue
<!-- 子组件 TodoItem.vue -->
<template>
<div class="todo-item" :class="{ completed: props.completed }">
<input type="checkbox" v-model="props.completed" @change="handleChange">
<span>{{ props.content }}</span>
<button @click="handleDelete">删除</button>
</div>
</template>
<script setup>
// 定义 props
const props = defineProps({
content: {
type: String,
required: true
},
completed: {
type: Boolean,
default: false
},
index: {
type: Number,
required: true
}
})
// 定义 emit
const emit = defineEmits(['change', 'delete'])
// 触发事件(传递参数)
const handleChange = () => {
emit('change', props.index, props.completed)
}
const handleDelete = () => {
emit('delete', props.index)
}
</script>
vue
<!-- 父组件 TodoList.vue -->
<template>
<div class="todo-list">
<h3>待办列表</h3>
<input
type="text"
v-model="inputVal"
@keyup.enter="addTodo"
placeholder="请输入待办内容"
>
<TodoItem
v-for="(item, index) in todoList"
:key="index"
:content="item.content"
:completed="item.completed"
:index="index"
@change="handleTodoChange"
@delete="handleTodoDelete"
/>
</div>
</template>
<script setup>
import { ref } from 'vue'
import TodoItem from './TodoItem.vue'
// 响应式数据
const inputVal = ref('')
const todoList = ref([
{ content: '学习 setup 语法糖', completed: false },
{ content: '完成 Vue3 实战', completed: true }
])
// 添加待办
const addTodo = () => {
if (!inputVal.value.trim()) return
todoList.value.push({
content: inputVal.value,
completed: false
})
inputVal.value = ''
}
// 修改待办状态
const handleTodoChange = (index, completed) => {
todoList.value[index].completed = completed
}
// 删除待办
const handleTodoDelete = (index) => {
todoList.value.splice(index, 1)
}
</script>
4.3 场景3:Vue3 + TS 组合使用(完整示例)
vue
<template>
<div class="user-info">
<h3>用户信息</h3>
<p>姓名:{{ user.name }}</p>
<p>年龄:{{ user.age }}</p>
<p>性别:{{ user.gender }}</p>
<button @click="updateAge">年龄+1</button>
</div>
</template>
<script setup lang="ts">
// 定义用户类型接口
interface User {
name: string
age: number
gender: 'male' | 'female'
}
// 响应式数据(指定类型)
import { ref, computed } from 'vue'
const user = ref<User>({
name: '张三',
age: 20,
gender: 'male'
})
// 计算属性(类型自动推导)
const isAdult = computed(() => user.value.age >= 18)
// 方法(指定参数和返回值类型)
const updateAge = (): void => {
user.value.age++
}
// 打印用户信息(类型校验)
console.log(user.value.name)
// console.log(user.value.address) // TS 报错,User 接口中无 address 属性
</script>
五、避坑指南:setup 语法糖常见错误与解决方案
在使用 setup 语法糖的过程中,新手容易遇到一些问题,这里整理了最常见的 5 个坑,附上解决方案,帮助你避免踩坑。
5.1 坑1:解构 reactive 数据导致响应式丢失
错误表现:解构 reactive 包装的对象后,修改解构后的变量,模板不更新。
vue
<script setup>
import { reactive } from 'vue'
const user = reactive({ name: '张三', age: 20 })
// 错误:直接解构,响应式丢失
const { name, age } = user
const changeName = () => {
name = '李四' // 模板不更新
}
</script>
解决方案:使用 toRefs 或 toRef 解构,修改时加 .value。
vue
<script setup>
import { reactive, toRefs } from 'vue'
const user = reactive({ name: '张三', age: 20 })
// 正确:使用 toRefs 解构
const { name, age } = toRefs(user)
const changeName = () => {
name.value = '李四' // 模板正常更新
}
</script>
5.2 坑2:忘记给 ref 变量加 .value 修改
错误表现:修改 ref 包装的变量时,忘记加 .value,导致变量修改失败,模板不更新。
vue
<script setup>
import { ref } from 'vue'
const count = ref(0)
const increment = () => {
// 错误:忘记加 .value
count++ // 无效,count 仍为 0
}
</script>
解决方案:修改 ref 变量时,必须加 .value;模板中使用时无需加。
vue
<script setup>
import { ref } from 'vue'
const count = ref(0)
const increment = () => {
// 正确:加 .value
count.value++
}
</script>
5.3 坑3:同时使用两个 script 标签,未区分功能
错误表现:在一个组件中使用两个 script 标签,且都写了组合式逻辑,导致逻辑冲突、变量未定义。
vue
<!-- 错误写法 -->
<script>
// 传统 script 标签写组合式逻辑
import { ref } from 'vue'
const count = ref(0)
</script>
<script setup>
// 语法糖中也写逻辑,导致 count 未定义
console.log(count) // 报错:count is not defined
</script>
解决方案:两个 script 标签分工明确------传统 script 标签仅用于配置组件选项(name、props 等),setup 语法糖用于编写组合式逻辑,且组合式逻辑仅在 setup 语法糖中编写。
5.4 坑4:使用 this 访问组件实例
错误表现:在 setup 语法糖中使用 this,试图访问 props、emit、attrs 等,导致报错(this is undefined)。
vue
<script setup>
// 错误:使用 this
console.log(this.props.name) // 报错:Cannot read property 'props' of undefined
</script>
解决方案:放弃使用 this,通过 defineProps、defineEmits、useAttrs 等 API 访问对应内容。
5.5 坑5:组件导入后未使用,导致报错
错误表现:导入组件后未在模板中使用,Vue3 会报"组件未使用"的警告,部分严格模式下会报错。
vue
<script setup>
// 错误:导入组件但未使用
import HelloWorld from './HelloWorld.vue'
</script>
解决方案:要么在模板中使用导入的组件,要么删除未使用的导入语句;如果确实需要导入但不使用(如动态导入备用组件),可在导入语句后加 // @ts-ignore(TS 项目)或忽略警告。
六、总结与拓展
6.1 核心总结
<script setup> 语法糖是 Vue3 组合式 API 的简化写法,核心优势是简化代码、提升效率,无需手动 return 暴露、无需手动注册组件,与 TypeScript 结合良好,是当前 Vue3 开发的主流选择。
核心知识点梳理:
- 基础用法:添加 setup 关键字,顶层变量/方法自动暴露,无需 return。
- 组件导入:自动注册,支持重命名、动态导入。
- 响应式数据:ref(基本类型)、reactive(引用类型),解构用 toRefs/toRef。
- 进阶特性:defineProps(props 定义)、defineEmits(事件触发)、defineSlots(插槽定义)。
- 避坑重点:避免解构 reactive 丢失响应式、ref 变量修改加 .value、不使用 this。
6.2 拓展延伸
-
与其他 API 结合:setup 语法糖可与 Vue3 其他 API 无缝结合,如
useRouter(路由)、useStore(Pinia/Vuex)、useFetch(请求)等,后续会单独讲解。 -
生命周期钩子:setup 语法糖中,可直接使用 Vue3 的生命周期钩子(如 onMounted、onUpdated 等),无需注册,直接导入使用即可。
-
兼容性:setup 语法糖从 Vue3.2 版本开始支持,如果你使用的是 Vue3.0 或 3.1 版本,需升级 Vue 版本才能使用。
掌握 setup 语法糖,能大幅提升你的 Vue3 开发效率,减少冗余代码,让组合式逻辑更清晰。建议多动手实战,结合本文的示例,尝试在项目中使用,快速吃透所有用法。