Skip to content

Vue 3 setup 深入解析与原理解析

一、核心要点速览

💡 核心考点

  • 什么是 setup: 组合式 API (Composition API) 的入口点,在组件创建之前执行。
  • <script setup>: Vue 3.2+ 引入的编译时语法糖,大幅减少了样板代码。
  • 运行时原理: setup() 接收 propscontext 参数,返回的对象会被暴露给模板。
  • 执行时机: 处于 beforeCreate 之前,此时无法访问 this

二、setup 的双重身份:编译时与运行时

在 Vue 3 中,我们通常有两种方式使用 setup。理解它们的区别是掌握 Vue 3 底层原理的关键。

Vue 3 setup 核心机制与原理解析

1. 编译时语法糖:<script setup>

这是目前官方推荐的写法。它是一个编译时的转换过程。

  • 核心作用:Vue Compiler 会将 <script setup> 内部的代码包装到一个隐藏的 setup() 函数中。
  • 自动暴露:在顶级作用域声明的所有变量、函数、导入的组件,都会被自动暴露给模板使用,无需显式 return
  • Compiler Macros:引入了 definePropsdefineEmits 等编译器宏,它们在编译时会被替换为相应的运行时声明。
vue
<script setup>
import { ref } from 'vue'
import MyComponent from './MyComponent.vue' // 自动注册

const count = ref(0) // 自动暴露给模板
const increment = () => count.value++

// 编译器宏:不需要 import,编译时处理
const props = defineProps(['title'])
</script>

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

2. 运行时入口:setup() 函数

这是组合式 API 的基础。如果你不使用 <script setup>,就需要显式定义 setup() 函数。

  • 接收参数
    1. props: 响应式的 props 对象。
    2. context: 一个包含 attrsslotsemitexpose 的非响应式对象。
  • 返回值
    • 如果返回一个对象:该对象的所有属性都会被合并到渲染上下文中。
    • 如果返回一个渲染函数:可以直接用它来定义组件的输出(通常用于 JSX/TSX)。
javascript
export default {
  props: ['title'],
  setup(props, { emit }) {
    const count = ref(0)
    
    // 必须显式返回
    return {
      count,
      increment: () => count.value++
    }
  }
}

三、setup 与模板的关系:渲染上下文 (Rendering Context)

无论你使用哪种写法,最终 Vue 都会创建一个渲染上下文

  1. 执行 setup:Vue 调用 setup() 函数。
  2. 获取 Bindings:收集 setup 返回的所有变量。
  3. 代理访问:模板编译生成的渲染函数中,对变量的访问(如 )会被代理到这个返回的对象上。

💡 关键区别

  • setup() 函数中,你需要通过 count.value 访问响应式数据。
  • 在模板中,Vue 会自动解包 (Unwrapping),所以你只需要写

四、高频面试题

1. setup 执行时机是在什么时候?为什么不能访问 this?

回答: setupbeforeCreate 生命周期钩子之前执行。由于此时组件实例(Internal Component Instance)尚未完全创建,Vue 故意没有绑定 this。这样做的目的是为了强制开发者使用组合式 API 的方式思考,避免 this 指向混乱。

2. <script setup> 相比普通 setup() 函数有哪些优势?

回答:

  1. 代码更简洁:无需显式返回对象,减少了样板代码。
  2. 更好的性能:由于模板直接可以使用 setup 中的变量,无需经过 Proxy 代理,编译出的代码效率更高。
  3. 更好的类型推导:对 TypeScript 支持更友好。
  4. 自动组件注册:导入的组件直接在模板可用。

3. setup 中的 props 是响应式的吗?解构 props 会发生什么?

回答: props 是响应式的。但是不能使用 ES6 解构(如 const { title } = props),因为解构会使变量丢失响应性。

  • 解决:如果需要解构,应使用 toRefstoRef(例如:const { title } = toRefs(props))。

4. defineProps 和 defineEmits 为什么不需要从 vue 导入?

回答: 因为它们是编译器宏 (Compiler Macros)。它们只在 <script setup> 中可用,Vue 编译器在扫描代码时会识别它们,并在编译阶段将其转换为运行时代码,所以不需要在运行时真正导入它们。

5. setup 内部如何获取插槽 (slots) 和自定义事件 (emit)?

回答:

  • 在普通 setup(props, context) 中,通过 context.slotscontext.emit 获取。
  • <script setup> 中,使用 useSlots()useEmits()(或 defineEmits)获取。

五、总结

  • <script setup> 是编译时的魔法,让代码更清爽 🪄
  • setup() 是运行时的锚点,是逻辑组织的根基 ⚓
  • 它们共同构成了 Vue 3 强大的组合式开发模式,让代码复用变得从未如此简单。
最近更新