Vue 3 组合式 API:setup、ref 与 reactive

FreeGuideOnline 最新 2026-06-15

Vue 3 组合式 API 上手指南

组合式 API 是 Vue 3 的核心设计范式,它通过函数式组织逻辑,让组件的开发变得更加灵活、可复用且易于维护。本教程将带你从零起步,掌握最核心的 setuprefreactive 以及它们的典型应用。


为什么需要组合式 API?

在 Vue 2 的选项式 API 中,组件的逻辑按 datamethodscomputed 等选项切割。当组件变得复杂时,同一功能的代码会分散在不同选项中,难以阅读和维护。组合式 API 允许你将同一个功能的所有逻辑写在一起,然后自由组合,从根本上解决了代码碎片化的问题。

此外,它让你能够轻松地将逻辑抽取为可复用的组合函数(Composables),在多个组件间共享有状态的逻辑。


setup:组件的入口

setup 是组合式 API 的起点,它会在组件实例被完全初始化之前执行。你可以在这里定义响应式数据、计算属性、方法,并将它们返回给模板。

<script>
export default {
  setup() {
    // 逻辑写在这里
    return {
      // 暴露给模板的变量与方法
    }
  }
}
</script>

<script setup> 语法糖出现后,代码会更加简洁,推荐所有新项目直接使用该语法

<script setup>
// 顶层变量和导入都会自动暴露给模板
</script>

下面我们均采用 <script setup> 风格进行演示。


ref:包装基本类型与响应式数据

ref 用于将任意类型的值包装成响应式引用。在组合式 API 中,它是使用最频繁的工具函数。

创建 ref

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

const count = ref(0)
const message = ref('你好')
</script>

ref 被传入的值会被存入 .value 属性中。在 <script> 中操作数据时,必须使用 .value

count.value++              // 触发更新
console.log(count.value)   // 读取当前值

而在 <template> 模板中,Vue 会自动解包 .value,直接写变量名即可:

<template>
  <p>计数:{{ count }}</p>
  <button @click="count++">+1</button>
</template>

原理探究

ref 内部使用一个对象包装原始值,并通过 gettersetter 追踪依赖与触发更新。对于对象类型,ref 会调用 reactive 处理其内部值,所以深层属性也是响应式的。


reactive:定义可响应的对象

reactive 返回一个深层响应式的对象代理,适合处理包含多个属性的结构化数据。

<script setup>
import { reactive } from 'vue'

const state = reactive({
  user: {
    name: '小明',
    age: 18
  },
  isLoggedIn: false
})
</script>

访问 reactive 包裹的对象时,无需使用 .value,直接读取和修改属性即可:

state.user.name = '小红'
state.isLoggedIn = true

reactive 的局限:

  • 只能用于对象、数组和 Map/Set 等集合类型,基本类型无效。
  • 不能直接替换整个对象(会失去响应性),若需要替换,建议使用 ref 包裹对象。
  • 解构后响应性会丢失。如果需要解构,要用 toRefs 保持响应。
import { toRefs } from 'vue'
const { user, isLoggedIn } = toRefs(state)   // 现在解构出的也是 ref

如何选择 ref 与 reactive?

场景 推荐工具
基本类型(字符串、数字等) ref
单个对象或数组 refreactive 均可,习惯上用 ref 更一致
表单数据、需要解构的场景 reactive + toRefs 或直接用多个 ref
逻辑复用时暴露接口 通常返回 ref 组成的对象,易于解构和追踪

统一风格很重要:大型项目中,许多团队选择只使用 ref,因为它 .value 的身影永远清晰,且对解构友好。


组合式 API 中的计算属性与侦听器

computed

<script setup>
import { ref, computed } from 'vue'

const price = ref(100)
const quantity = ref(2)
const total = computed(() => price.value * quantity.value)
</script>

computed 返回的也是一个 ref 类型,读取时需用 .value(模板中自动解包)。

watch 与 watchEffect

<script setup>
import { ref, watch, watchEffect } from 'vue'

const keyword = ref('')

// 侦听特定数据源
watch(keyword, (newVal, oldVal) => {
  console.log(`搜索关键词变为:${newVal}`)
})

// 自动追踪内部依赖
watchEffect(() => {
  console.log(`当前输入内容:${keyword.value}`)
})
</script>
  • watch 需要明确指定侦听源,可获取新旧值,惰性执行。
  • watchEffect 会自动收集内部用到的响应式依赖,立即执行,适合副作用。

生命周期钩子

在组合式 API 中,生命周期钩子变成了按需导入的函数,命名方式为 onMountedonUpdatedonUnmounted 等。

<script setup>
import { onMounted, onUnmounted } from 'vue'

onMounted(() => {
  console.log('组件已挂载')
})

onUnmounted(() => {
  console.log('组件已卸载')
})
</script>

它们可以在 setup 中多次调用,所有回调会按顺序执行。


组合函数:逻辑复用的终极武器

将一组相关的状态、计算属性和方法提取到单独的函数中,即可创建可复用的组合函数。

// useMouse.js
import { ref, onMounted, onUnmounted } from 'vue'

export function useMouse() {
  const x = ref(0)
  const y = ref(0)

  function update(event) {
    x.value = event.pageX
    y.value = event.pageY
  }

  onMounted(() => window.addEventListener('mousemove', update))
  onUnmounted(() => window.removeEventListener('mousemove', update))

  return { x, y }
}

在组件中使用:

<script setup>
import { useMouse } from './useMouse'
const { x, y } = useMouse()
</script>

<template>
  鼠标位置{{ x }}, {{ y }}
</template>

这种模式让你能够将任何可复用的逻辑打包为独立模块,大大提升开发效率和代码可维护性。


小结

  • setup 是组合式 API 的入口,推荐使用 <script setup> 语法。
  • ref 用于包装任意类型为响应式引用,访问/修改需要 .value
  • reactive 创建深层响应式对象,直接操作属性但解构会丢失响应。
  • 配合 computedwatchwatchEffect 和生命周期钩子,可以构建出功能完整的组件逻辑。
  • 将逻辑抽取为组合函数,是组合式 API 带来的最大红利,让你的代码更像搭积木一样组装功能。

掌握这些核心概念后,你就已经跨过了组合式 API 的门槛,可以着手构建更加清晰、可扩展的 Vue 3 应用了。