Vue 3 组合式 API:setup、ref 与 reactive
Vue 3 组合式 API 上手指南
组合式 API 是 Vue 3 的核心设计范式,它通过函数式组织逻辑,让组件的开发变得更加灵活、可复用且易于维护。本教程将带你从零起步,掌握最核心的 setup、ref、reactive 以及它们的典型应用。
为什么需要组合式 API?
在 Vue 2 的选项式 API 中,组件的逻辑按 data、methods、computed 等选项切割。当组件变得复杂时,同一功能的代码会分散在不同选项中,难以阅读和维护。组合式 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 内部使用一个对象包装原始值,并通过 getter 和 setter 追踪依赖与触发更新。对于对象类型,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 |
| 单个对象或数组 | ref 或 reactive 均可,习惯上用 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 中,生命周期钩子变成了按需导入的函数,命名方式为 onMounted、onUpdated、onUnmounted 等。
<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创建深层响应式对象,直接操作属性但解构会丢失响应。- 配合
computed、watch、watchEffect和生命周期钩子,可以构建出功能完整的组件逻辑。 - 将逻辑抽取为组合函数,是组合式 API 带来的最大红利,让你的代码更像搭积木一样组装功能。
掌握这些核心概念后,你就已经跨过了组合式 API 的门槛,可以着手构建更加清晰、可扩展的 Vue 3 应用了。