Vue3 `<script setup>`语法糖详解与最佳实践

张辰LMY

1. Vue3 <script setup> 语法糖的革命性变化

在Vue3的Composition API中,<script setup> 语法糖彻底改变了我们编写组件的方式。这个特性自Vue 3.2版本正式发布以来,已经成为现代Vue开发的标配写法。它通过编译时的语法转换,让我们可以用更简洁的代码实现相同的功能。

传统Options API写法需要在export default中定义各种选项,而Composition API虽然提供了更好的逻辑组织方式,但仍然需要显式地返回模板中使用的变量和方法。<script setup>的出现解决了这个问题,它让组件的编写变得更加直观和高效。

重要提示:<script setup>是在单文件组件(SFC)中使用的编译时语法糖,它会在编译阶段被转换为标准的Composition API代码。这意味着它不会增加运行时开销,却能提供更好的开发体验。

1.1 为什么可以省略defineComponent

在标准Composition API中,我们通常需要使用defineComponent来定义组件:

javascript复制import { defineComponent } from 'vue'

export default defineComponent({
  setup() {
    // 组件逻辑
    return {
      // 暴露给模板的内容
    }
  }
})

而在<script setup>中,这一切都变得简单了:

vue复制<script setup>
// 直接在这里写组件逻辑
</script>

这种写法之所以可行,是因为<script setup>在编译时会自动将所有顶层绑定(变量、函数等)暴露给模板。编译器会为我们处理所有必要的包装工作,包括自动推断组件选项、处理props和emits等。

2. <script setup>的核心特性解析

2.1 自动的顶层绑定暴露

<script setup>中声明的任何顶级变量、函数或import都会自动成为模板的可用内容。这意味着你不再需要手动从setup函数中返回它们:

vue复制<script setup>
import { ref } from 'vue'

const count = ref(0)
const increment = () => count.value++

// 这些都会自动暴露给模板
</script>

<template>
  <button @click="increment">{{ count }}</button>
</template>

这种自动暴露机制大大简化了代码,减少了样板代码的编写。编译器会分析<script setup>块中的所有顶层绑定,并自动生成对应的模板作用域。

2.2 编译器宏的使用

<script setup>引入了一系列编译器宏,这些特殊的函数在编译时会被处理,不会出现在运行时代码中:

  1. defineProps - 定义组件接收的props
  2. defineEmits - 定义组件触发的事件
  3. defineExpose - 明确指定组件暴露给父组件的内容
  4. useSlotsuseAttrs - 访问插槽和属性

这些宏之所以不需要导入就能使用,是因为它们是由Vue编译器特殊处理的。在编译阶段,它们会被转换为标准的组件选项。

2.2.1 defineProps的典型用法

vue复制<script setup>
const props = defineProps({
  title: {
    type: String,
    required: true
  },
  count: {
    type: Number,
    default: 0
  }
})
</script>

2.2.2 defineEmits的典型用法

vue复制<script setup>
const emit = defineEmits(['update', 'delete'])

function handleClick() {
  emit('update', newValue)
}
</script>

注意事项:这些编译器宏必须在<script setup>的顶层作用域中使用,不能在函数内部或其他块级作用域中使用,否则会导致编译错误。

3. 与传统写法的对比分析

3.1 代码量对比

让我们通过一个简单的计数器组件来对比不同写法的代码量:

Options API写法:

vue复制<script>
export default {
  data() {
    return {
      count: 0
    }
  },
  methods: {
    increment() {
      this.count++
    }
  }
}
</script>

标准Composition API写法:

vue复制<script>
import { defineComponent, ref } from 'vue'

export default defineComponent({
  setup() {
    const count = ref(0)
    const increment = () => count.value++
    
    return {
      count,
      increment
    }
  }
})
</script>