02-组合式API详解
Vue 3 组合式 API 详解
1. 什么是组合式 API
组合式 API(Composition API)是 Vue 3 引入的一组新的 API,它允许开发者使用函数的方式来组织和复用组件逻辑。与 Options API 不同,组合式 API 更加灵活,便于逻辑复用和代码组织。
2. setup() 函数
基本用法
<script> import { ref } from 'vue'export default {
setup() {
const count = ref(0)function increment() {
count.value++
}
return {
count,
increment
}
}
}
</script>
<script setup> 语法糖
<script setup> import { ref } from 'vue'const count = ref(0)
function increment() {
count.value++
}
</script>
<template>
<button @click="increment">{{ count }}</button>
</template>
3. 响应式 API
ref()
用于创建基本类型的响应式引用:
import { ref } from 'vue'const count = ref(0)
const message = ref('Hello')
const isVisible = ref(true)// 在 JS 中通过 .value 访问
console.log(count.value)
// 在模板中直接访问
// <template>{{ count }}</template>
reactive()
用于创建对象类型的响应式代理:
import { reactive } from 'vue'const state = reactive({
count: 0,
name: 'Vue',
items: []
})
// 直接访问属性
state.count++
state.name = 'Vue 3'
state.items.push('item1')
ref vs reactive 对比
| 特性 | ref | reactive |
|------|-----|----------|
| 适用类型 | 任意类型 | 对象/数组 |
| 访问方式 | .value | 直接访问 |
| 可重新赋值 | ✅ | ❌(会失去响应性)|
| 解构保持响应性 | ❌ | ❌(需用 toRefs)|
toRefs()
将响应式对象的属性转换为 ref:
import { reactive, toRefs } from 'vue'const state = reactive({
count: 0,
name: 'Vue'
})// 解构时保持响应性
const { count, name } = toRefs(state)
count.value++ // 会更新 state.count
4. 计算属性 computed()
<script setup> import { ref, computed } from 'vue'const firstName = ref('张')
const lastName = ref('三')// 只读计算属性
const fullName = computed(() => {
return firstName.value + ' ' + lastName.value
})
// 可写计算属性
const fullNameWritable = computed({
get() {
return firstName.value + ' ' + lastName.value
},
set(newValue) {
const [first, last] = newValue.split(' ')
firstName.value = first
lastName.value = last
}
})
</script>
5. 侦听器 watch()
侦听单个数据源
<script setup> import { ref, watch } from 'vue'const keyword = ref('')
watch(keyword, (newValue, oldValue) => {
console.log('新值:', newValue)
console.log('旧值:', oldValue)
})
</script>
侦听多个数据源
import { ref, watch } from 'vue'const x = ref(0)
const y = ref(0)
watch([x, y], ([newX, newY], [oldX, oldY]) => {
console.log(x: ${oldX} -> ${newX})
console.log(y: ${oldY} -> ${newY})
})
深度侦听
import { reactive, watch } from 'vue'const state = reactive({
user: {
name: '张三',
address: {
city: '北京'
}
}
})
// reactive 对象默认深度侦听
watch(state, (newState) => {
console.log('状态变化:', newState)
})
立即执行
watch(keyword, (newValue) => {
console.log('立即执行:', newValue)
}, { immediate: true })
6. 侦听器 watchEffect()
自动追踪依赖并执行:
import { ref, watchEffect } from 'vue'const count = ref(0)
const name = ref('Vue')
// 自动追踪 count 和 name
watchEffect(() => {
console.log(count: ${count.value}, name: ${name.value})
})
清除副作用
watchEffect((onCleanup) => { const controller = new AbortController()fetch(
/api/data?q=${keyword.value}, {
signal: controller.signal
})
.then(res => res.json())
.then(data => {
results.value = data
})
// 下次副作用运行时或侦听器停止时调用
onCleanup(() => controller.abort())
})
7. 生命周期钩子
<script setup> import { onMounted, onUpdated, onUnmounted, onBeforeMount, onBeforeUpdate, onBeforeUnmount } from 'vue'onBeforeMount(() => {
console.log('挂载前')
})onMounted(() => {
console.log('挂载完成')
// 适合:DOM 操作、发起网络请求、添加事件监听
})onBeforeUpdate(() => {
console.log('更新前')
})onUpdated(() => {
console.log('更新完成')
})onBeforeUnmount(() => {
console.log('卸载前')
// 适合:清理定时器、移除事件监听
})
onUnmounted(() => {
console.log('卸载完成')
})
</script>
8. 模板引用 ref
<script setup> import { ref, onMounted } from 'vue'const inputRef = ref(null)
const childRef = ref(null)onMounted(() => {
// 访问 DOM 元素
inputRef.value.focus()// 访问子组件实例
childRef.value.childMethod()
})
</script>
<template>
<input ref="inputRef" type="text" />
<ChildComponent ref="childRef" />
</template>
9. 自定义 Hook(组合函数)
鼠标位置追踪
// 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>
<p>鼠标位置: {{ x }}, {{ y }}</p>
</template>
异步请求
// useFetch.js import { ref, watchEffect } from 'vue'export function useFetch(url) {
const data = ref(null)
const error = ref(null)
const loading = ref(true)async function fetchData() {
loading.value = true
error.value = nulltry {
const response = await fetch(url.value)
if (!response.ok) throw new Error('请求失败')
data.value = await response.json()
} catch (e) {
error.value = e.message
} finally {
loading.value = false
}
}watchEffect(() => {
fetchData()
})
return { data, error, loading }
}
<script setup>
import { useFetch } from './useFetch'
import { computed } from 'vue'
const userId = ref(1)
const url = computed(() => `https://api.example.com/users/${userId.value}`)
const { data, error, loading } = useFetch(url)
</script>
<template>
<div v-if="loading">加载中...</div>
<div v-else-if="error">错误: {{ error }}</div>
<div v-else>
<p>姓名: {{ data.name }}</p>
<p>邮箱: {{ data.email }}</p>
</div>
</template>
10. provide / inject
基础用法
<!-- 父组件 --> <script setup> import { provide, ref } from 'vue'const theme = ref('light')
const user = ref({ name: '张三' })provide('theme', theme)
provide('user', user)
function toggleTheme() {
theme.value = theme.value === 'light' ? 'dark' : 'light'
}
</script>
<!-- 子孙组件(任意层级) -->
<script setup>
import { inject } from 'vue'
const theme = inject('theme')
const user = inject('user')
// 提供默认值
const config = inject('config', { language: 'zh-CN' })
</script>
<template>
<p>当前主题: {{ theme }}</p>
<p>用户: {{ user.name }}</p>
</template>
小结
组合式 API 是 Vue 3 的核心特性,它提供了更灵活的代码组织方式:
- setup() 和
<script setup>是组合式 API 的入口 - 响应式 API(ref、reactive)管理状态
- computed 和 watch 处理派生状态和副作用
- 生命周期钩子 处理组件生命周期事件
- 自定义 Hook 实现逻辑复用
- provide/inject 实现跨层级通信

浙公网安备 33010602011771号