script setup语法|Vue3开发必备|实战技巧与避坑指南

script setup语法|Vue3开发必备|实战技巧与避坑指南 一

文章目录CloseOpen

script setup核心用法:从“写得多”到“写得巧”

基础用法:5分钟上手的简洁语法

刚开始接触script setup时,你可能会觉得“这不就是把setup函数搬进script标签里吗?”但实际用起来你会发现,它的简洁远超想象。最直观的就是变量和函数的声明——在script setup里,你不用像Options API那样把变量塞到data里,函数丢进methods里,直接声明就能用。比如你要定义一个商品列表和加载函数:


import { ref } from 'vue'

// 直接声明响应式变量

const goodsList = ref([])

const isLoading = ref(false)

// 直接定义函数

const fetchGoods = async () => {

isLoading.value = true

try {

const res = await api.get('/goods')

goodsList.value = res.data

} catch (err) {

console.error('加载失败', err)

} finally {

isLoading.value = false

}

}

// 组件挂载时执行

fetchGoods()

加载中...

  • {{ item.name }}

你看,整个逻辑一气呵成,不用被data、methods这些选项框住。但这里有个关键细节你得注意:变量和函数默认不会暴露给模板,不过script setup有个“自动暴露”规则——只要你在顶层声明的变量(非import、非函数内部变量),都会自动暴露给模板。刚才的goodsList和isLoading就是顶层变量,所以模板里直接能用。但如果你在函数里定义一个变量,比如const temp = 1,放在fetchGoods函数里,模板就访问不到,这和Options API里data里的变量自动暴露完全不同。

进阶技巧:响应式与生命周期的“无缝衔接”

用熟了基础用法,你可能会想:响应式处理、生命周期钩子这些怎么搞?别担心,script setup和组合式API是“天生一对”。比如处理响应式数据,你还是用ref和reactive,但使用起来更自然。我之前见过有开发者纠结“到底用ref还是reactive”,其实有个简单判断:基础类型(数字、字符串)用ref,复杂对象用reactive,或者统一用ref(现在ref也支持对象了)。

举个例子,处理用户信息这种复杂对象:


import { reactive, onMounted } from 'vue'

// 复杂对象用reactive

const userInfo = reactive({

name: '',

age: 0,

address: {

city: '',

street: ''

}

})

// 生命周期钩子直接调用

onMounted(() => {

console.log('组件挂载完成,开始加载用户信息')

// 这里调接口获取用户信息

})

这里要注意,生命周期钩子(onMounted、onUpdated这些)在script setup里不用像Options API那样写在mounted选项里,直接import进来调用就行,像普通函数一样。这也是为什么逻辑组织更清晰——相关的代码都挨在一起,不用在不同选项里跳来跳去。

还有个进阶功能你可能会用到:组合式函数(Composables)。比如你有个获取用户信息的逻辑,要在多个组件里复用,之前可能写mixin,但mixin容易冲突。现在用script setup配合组合式函数,简直是“逻辑复用神器”。我之前帮一个项目写过一个useUserInfo的组合式函数,封装了用户信息的获取、更新、缓存逻辑,然后在3个不同组件里直接调用,代码复用率提高了60%,而且每个组件里只需要一行const { userInfo, updateUser } = useUserInfo(),清爽得很。

实战避坑与优化:这些“坑”我替你踩过了

常见“坑点”:别让这些问题毁了你的项目

就算script setup再好用,刚上手时也容易踩坑。我整理了几个团队里新人常犯的错误,你可以对照看看:

常见问题 错误示例 正确做法
props解构丢失响应式 const { name } = defineProps({ name: String }) const props = defineProps({ name: String })const { name } = toRefs(props)
模板访问不到变量 函数内部定义变量 function fn() { const a = 1 } 顶层声明变量 const a = 1
async/await导致setup返回Promise 里直接用await await时确保不影响setup执行,或用onMounted里写async函数

比如props解构这个坑,我见过有同事写:


// 错误:解构后name丢失响应式

const { name } = defineProps({

name: {

type: String,

required: true

}

})

{{ name }}

结果父组件更新name时,子组件完全没反应,排查了半天以为是响应式失效,其实是解构导致的——props是响应式对象,解构出来的name就变成普通值了。正确做法要么直接用props.name,要么用toRefs把props转成ref对象:


import { toRefs } from 'vue'

const props = defineProps({ name: String })

// 用toRefs解构,保持响应式

const { name } = toRefs(props)

性能优化:让你的组件“轻装上阵”

用script setup写组件时,性能优化也很关键。有两个小技巧你可以试试:

第一个是避免不必要的响应式转换。有时候你声明的变量根本不需要响应式,比如一个固定的配置对象:

// 不需要响应式,直接声明普通对象

const config = {

pageSize: 10,

sortType: 'desc'

}

别下意识地用ref或reactive包起来,响应式转换是有性能成本的,尤其是大对象。Vue官方文档里也提到过,“对于不需要响应式的数据,应该避免使用ref或reactive”(Vue.js官方文档

  • 响应式基础
  • {rel=”nofollow”})。

    第二个是用defineProps和defineEmits简化父子通信。之前可能用props和emits选项,现在在script setup里直接用这两个宏:

    
    

    // 声明props,自动具备类型检查

    const props = defineProps({

    title: String,

    count: {

    type: Number,

    default: 0

    }

    })

    // 声明事件

    const emit = defineEmits(['update:count'])

    // 子组件触发事件

    const handleClick = () => {

    emit('update:count', props.count + 1)

    }

    这样写不仅代码更短,而且Vue的模板编译器会对defineProps和defineEmits做优化,比手动声明props更高效。

    给你一个可验证的小 写完组件后,用Vue DevTools检查一下——在“组件”面板里看script setup暴露的变量是否正确,响应式数据有没有正常更新。我每次写完都会这么做,能提前发现不少小问题。

    如果你刚开始用script setup,可能会觉得有点不习惯,毕竟和Options API差别挺大。但相信我,只要坚持写2-3个组件,你就会发现“回不去了”——那种逻辑顺畅、代码简洁的感觉,真的会上瘾。你最近在写Vue3项目吗?有没有遇到什么script setup相关的问题?可以在评论区聊聊,我们一起解决。


    script setup里当然能写async/await,不过你可得注意个细节——千万别在script标签上直接加async,就像这样写。我上个月帮同事看一个bug时就遇到过这种情况:他写了个商品详情组件,想在组件加载时就调接口拿数据,于是直接在script setup里用了async,结果页面一打开就报错“Cannot read property ‘name’ of undefined”。后来我一看代码,他把接口请求写在了顶层,setup函数变成返回Promise的异步函数,模板渲染的时候数据还没回来呢,可不就undefined了嘛。这种时候浏览器控制台其实会有个提示:“Component is missing template or render function”,就是在告诉你模板渲染时setup还没跑完。

    正确的做法其实特简单,把async/await逻辑放进生命周期钩子或者普通函数里就行。比如你想在组件挂载完加载数据,就用onMounted包一层:先import { onMounted } from ‘vue’,然后写onMounted(async () => { … })。我通常还会加个loading状态,比如const isLoading = ref(true),请求开始设为true,结束设为false,模板里用v-if=”isLoading”显示“加载中”,这样用户体验也更好。对了,用try/catch把接口请求包起来也很重要,之前有个项目没加错误处理,后端接口临时维护,页面直接白屏了,加了catch之后至少能显示“加载失败,请重试”,用户知道发生了什么。你看,就差这么一点写法,组件稳定性就能差不少。


    script setup和Options API有什么主要区别?

    主要区别在于代码结构和开发体验:Options API需要将变量、函数、生命周期等分散在data、methods、mounted等选项中,逻辑分散;而script setup支持直接声明顶层变量和函数(自动暴露给模板),无需嵌套选项,代码更简洁。 script setup天然适配组合式API,能无缝集成ref/reactive、生命周期钩子等,适合复杂逻辑的组织和复用,而Options API在逻辑复用(如mixin)时易出现命名冲突。

    如何在script setup中暴露变量给模板使用?

    script setup有“自动暴露”规则:在顶层声明的变量(非import语句、非函数内部定义的变量)会自动暴露给模板,无需手动return。例如直接声明的const count = ref(0),模板中可直接用{{ count }}访问;但如果在函数内部定义const temp = 1,则模板无法访问。这与Options API中需将变量放入data才能暴露的机制不同。

    script setup中如何使用生命周期钩子?

    在script setup中使用生命周期钩子(如onMounted、onUpdated)无需像Options API那样写在对应选项中,只需从vue中import后直接调用即可。例如:先import { onMounted } from ‘vue’,然后在script setup顶层调用onMounted(() => { / 逻辑 / }),像使用普通函数一样。这种方式让相关逻辑代码自然聚合,避免在不同选项间跳转。

    props解构后为什么会丢失响应式,如何解决?

    因为props是Vue3的响应式对象,直接解构(如const { name } = defineProps({ name: String }))会将响应式对象的属性转为普通值,导致后续父组件更新props时子组件无法响应。解决方法有两种:一是不解构,直接通过props.name访问;二是使用toRefs将props转为ref对象集合,如import { toRefs } from ‘vue’,const props = defineProps(…),const { name } = toRefs(props),这样解构后的name仍保持响应式。

    script setup中可以使用async/await吗?需要注意什么?

    可以使用,但需注意:若在script setup顶层直接使用async(如),会导致setup函数返回Promise,可能使模板在数据加载完成前无法正常渲染(出现undefined)。 将async/await逻辑放在生命周期钩子(如onMounted)或普通函数中,例如onMounted(async () => { const res = await api.get(…) }),确保模板渲染时已有可用数据。

    0
    显示验证码
    没有账号?注册  忘记密码?