前言

Vue 3 带来的不仅是性能提升,更是组件编写范式的全面革新。本文从 Options API 到 Composition API 的演进切入,深入解析 <script setup> 语法糖、响应式系统与计算属性的核心机制,助你快速掌握现代 Vue 开发的最佳实践。

一、 组件基础与结构演进

1.1 Vue 2 传统的组件定义方式

在 Vue 2 中(结合 TypeScript 时),组件通常采用类或者对象配置的形式进行导出。

<script lang="ts">
export default {
  name: 'App' // 明确指定组件名,利于在开发者工具中调试和组件递归调用
}
</script>
  • 特点:属于典型的 Options API(配置式API),组件的名字、数据、方法等都要写在固定的配置项中。

1.2 Vue 3 模板新特性:打破单根节点限制 (Fragments)

  • Vue 2 痛点<template> 内部必须有且仅有一个根标签(通常包裹一个 <div>),否则编译报错。
  • Vue 3 优化:引入了 Fragments(片段) 特性。组件的模板中可以没有根标签,多个同级标签可以直接并列编写。
  • 好处:减少了无意义的 DOM 层级嵌套,优化了内存占用和渲染性能。

二、 架构范式:Options API vs. Composition API

2.1 Options API (配置式 API)

  • 代表版本:Vue 2
  • 编写方式:数据写在 data、方法写在 methods、计算属性写在 computed、监听器写在 watch
  • 痛点:当组件业务变得极其复杂时,同一功能模块的逻辑会被拆散到不同的配置项中。开发者在维护代码时,需要频繁地上下滚动页面,极其不利于代码的阅读和复用。

2.2 Composition API (组合式 API)

  • 代表版本:Vue 3
  • 编写方式:以功能为单位,将相同功能的响应式数据、方法、计算属性有机地组合在一起,甚至可以抽离成独立的 hooks 文件。
  • 优势
    • 完美的高聚合、低耦合,代码按业务逻辑聚集。
    • 极大地方便了代码的复用(取代了 Vue 2 中饱受诟病的 mixins)。
    • 对 TypeScript 的类型推导支持更加友好。

三、 核心语法糖:<script setup>

setup 是 Vue 3 组合式 API 的入口。普通的 setup() 函数需要手动返回(return)每一个变量和方法,模板才能使用,闭包写法较为繁琐。

Vue 3 推出了 <script setup> 语法糖

<script setup lang="ts">
// 在这里声明的变量、函数、import引入的组件,不需要手动 return,在 template 中可以直接使用!
const msg = 'Hello Vue 3!'
</script>

<template>
  <h1>{{ msg }}</h1>
</template>
  • 核心优势
    • 代码极其精简,告别了冗余的 export defaultreturn
    • 引入的子组件自动注册,直接在模板中写标签即可使用,无需显式声明 components

四、 响应式数据全景(Ref & Reactive)

4.1 ref 创建基本类型的响应式数据

  • 语法const count = ref(0)
  • 底层原理:基于 Object.defineProperty()getset 进行数据劫持,封装为 RefImpl(引用实现)对象。
  • 读写规则:在 <script> 中读写必须加上 .value;但在 <template> 模板中解析时,Vue 会自动解包,不需要.value

4.2 reactive 创建对象类型的响应式数据

  • 语法const state = reactive({ name: '张三', age: 18 })
  • 底层原理:基于 ES6 的 Proxy(代理) 实现,直接拦截整个对象的操作。
  • 读写规则:无论在 <script> 还是 <template> 中,都直接通过 state.name 访问,绝不使用 .value

4.3 ref 创建对象类型的响应式数据

  • 语法const userInfo = ref({ name: '李四' })
  • 底层原理:当把一个对象传给 ref 时,它内部会自动调用 reactive 来将其转化为 Proxy 代理对象。
  • 读写规则:在 <script> 中访问时,需要写成 userInfo.value.name

4.4 ref 与 reactive 的深度对比

维度 ref reactive
定义数据类型 基类类型(String、Number等)、对象类型 只能定义对象类型(Object、Array等)
访问方式 脚本中必须带 .value,模板中不用 任何地方都不带 .value
重新赋值(重写) 可以直接修改 .value 指向一个新对象(依然保持响应式) 不能直接赋新对象(会丢失响应式),需使用 Object.assign 或修改内部属性
解构支持 不适用 直接解构会丢失响应式

五、 响应式转换工具:toRef 与 toRefs

5.1 为什么需要它们?

当我们在组件中有一个 reactive 对象,如果直接在模板中写一长串前缀(如 {{ user.nested.info.name }})很麻烦。但如果我们尝试用 ES6 结构赋值:

const user = reactive({ name: 'Tom', age: 20 })
const { name, age } = user // 此时 name 和 age 变成了普通字符串和数字,丢失了响应式!

为了让对象解构出来的单体属性依然保持响应式,就需要使用 toReftoRefs

5.2 toRef

  • 作用:为响应式对象上的某个特定属性,创建一个对应的 ref。这个 ref 与源属性保持同步链接。
  • 语法const nameRef = toRef(user, 'name')

5.3 toRefs

  • 作用:批量处理。将一个 reactive 对象转换为一个普通对象,但这个普通对象的每一个属性都是一个对应的 ref
  • 语法
const user = reactive({ name: 'Tom', age: 20 })
// 批量转换并解构
const { name, age } = toRefs(user) 
// 此时 name 和 age 都是独立的 RefImpl 对象,且与 user 内部属性联动,可直接在模板中使用!

六、 高级响应式特性:computed 与 watch

6.1 computed 计算属性

  • 作用:根据已有的响应式数据,衍生出新的计算数据。
  • 特点具备缓存机制。只有当它依赖的响应式源数据发生改变时,它才会重新计算;否则多次访问会直接读取缓存,性能极高。
  • Vue 3 写法
import { ref, computed } from 'vue'

const firstName = ref('张')
const lastName = ref('三')

// 简写形式(只读)
const fullName = computed(() => {
  return firstName.value + lastName.value
})

6.2 watch 侦听器

  • 作用:监视一个或多个特定响应式数据的变化,当数据变化时执行特定的回调函数(侧重于执行业务旁路逻辑,如异步请求、本地存储等)。
  • Vue 3 写法特性
    • 可以直接监听 refreactive 对象、一个 Getter 函数,或者由它们组成的数组。
    • 提供了丰富的配置项,例如 deep: true(深度监听)、immediate: true(立即初始执行一次)。
import { ref, watch } from 'vue'

const count = ref(0)

// 监听单个 ref
watch(count, (newValue, oldValue) => {
  console.log(`count从 ${oldValue} 变为了 ${newValue}`)
})

结语

掌握 ref 与 reactive 的差异边界、toRefs 的解构技巧以及 computed 的缓存机制,是驾驭 Vue 3 响应式系统的关键。组合式 API 让代码按业务逻辑聚合,为复杂应用的维护与复用打开了全新空间。

更多推荐