Vue 3 Composition API 深度解析
引言
Vue 3 的 Composition API 是 Vue 框架最具革命性的更新之一。它解决了 Options API 在大型项目中代码组织、逻辑复用和类型推导等方面的痛点。本文将深入讲解 Composition API 的核心概念、使用技巧以及最佳实践。
什么是 Composition API?
Composition API 是一组基于函数的 API,允许我们将相关逻辑组织在一起,而不是将代码分散在 data、methods、computed 等选项中。
对比:Options API vs Composition API
Options API (Vue 2 风格):
export default {
data() {
return {
count: 0,
todos: []
}
},
methods: {
increment() {
this.count++
},
fetchTodos() {
// 获取待办事项
}
},
computed: {
doubleCount() {
return this.count * 2
}
},
mounted() {
this.fetchTodos()
}
}
Composition API (Vue 3 风格):
import { ref, computed, onMounted } from 'vue'
export default {
setup() {
const count = ref(0)
const todos = ref([])
const doubleCount = computed(() => count.value * 2)
const increment = () => {
count.value++
}
const fetchTodos = async () => {
// 获取待办事项
}
onMounted(() => {
fetchTodos()
})
return {
count,
todos,
doubleCount,
increment,
fetchTodos
}
}
}
核心 API 详解
1. ref() - 响应式基础类型
ref() 用于创建响应式的基本类型值。
import { ref } from 'vue'
const count = ref(0)
// 访问和修改
console.log(count.value) // 0
count.value = 1
// 在模板中自动解包,不需要 .value
关键点:
- 在 JavaScript 中需要通过
.value访问 - 在模板中自动解包
- 支持任意类型的值
2. reactive() - 响应式对象
reactive() 用于创建响应式对象。
import { reactive } from 'vue'
const state = reactive({
count: 0,
user: {
name: '张三',
age: 25
}
})
// 直接访问,不需要 .value
state.count++
state.user.age = 26
ref vs reactive :
-
ref可以处理任意类型,包括基本类型 -
reactive只能处理对象类型 -
ref需要.value,reactive直接访问
3. computed() - 计算属性
import { ref, computed } from 'vue'
const firstName = ref('张')
const lastName = ref('三')
const fullName = computed(() => {
return firstName.value + lastName.value
})
// 只读计算属性
console.log(fullName.value) // '张三'
// 可写计算属性
const fullNameWritable = computed({
get: () => firstName.value + lastName.value,
set: (val) => {
const [first, last] = val.split(' ')
firstName.value = first
lastName.value = last
}
})
4. watch() 和 watchEffect()
watch() - 精确监听:
import { ref, watch } from 'vue'
const count = ref(0)
// 监听单个值
watch(count, (newVal, oldVal) => {
console.log(`从 ${oldVal} 变为 ${newVal}`)
})
// 监听多个值
watch([count, firstName], ([newCount, newName]) => {
console.log('变化:', newCount, newName)
})
// 监听对象属性(深度监听)
const user = reactive({ profile: { name: '张三' } })
watch(
() => user.profile,
(newVal) => {
console.log('profile 变化', newVal)
},
{ deep: true }
)
watchEffect() - 自动追踪依赖:
import { ref, watchEffect } from 'vue'
const count = ref(0)
watchEffect(() => {
// 自动追踪 count 的变化
console.log('count 是:', count.value)
// 这里不需要指定监听谁,Vue 自动分析依赖
})
5. 生命周期钩子
Composition API 中的生命周期钩子需要在 setup() 中调用:
import {
onMounted,
onUpdated,
onUnmounted,
onBeforeMount,
onBeforeUpdate,
onBeforeUnmount,
onErrorCaptured
} from 'vue'
export default {
setup() {
onMounted(() => {
console.log('组件已挂载')
})
onUpdated(() => {
console.log('组件已更新')
})
onUnmounted(() => {
console.log('组件已卸载')
})
// 错误捕获
onErrorCaptured((err, instance, info) => {
console.error('捕获到错误:', err)
return false // 阻止错误继续传播
})
}
}
逻辑复用:组合式函数
Composition API 最大的优势在于逻辑复用。我们可以将相关逻辑封装成组合式函数(Composables)。
示例:useCounter
// composables/useCounter.js
import { ref, computed } from 'vue'
export function useCounter(initialValue = 0) {
const count = ref(initialValue)
const double = computed(() => count.value * 2)
const increment = () => count.value++
const decrement = () => count.value--
const reset = () => count.value = initialValue
return {
count,
double,
increment,
decrement,
reset
}
}
使用:
import { useCounter } from './composables/useCounter'
export default {
setup() {
const { count, double, increment, decrement } = useCounter(10)
return {
count,
double,
increment,
decrement
}
}
}
示例:useFetch
// composables/useFetch.js
import { ref, onMounted } from 'vue'
export function useFetch(url) {
const data = ref(null)
const loading = ref(true)
const error = ref(null)
const fetchData = async () => {
try {
loading.value = true
const response = await fetch(url)
data.value = await response.json()
} catch (err) {
error.value = err.message
} finally {
loading.value = false
}
}
onMounted(() => {
fetchData()
})
return {
data,
loading,
error,
refresh: fetchData
}
}
使用:
import { useFetch } from './composables/useFetch'
export default {
setup() {
const { data, loading, error, refresh } = useFetch('/api/users')
return {
data,
loading,
error,
refresh
}
}
}
实际应用场景
1. 表单处理
// composables/useForm.js
import { ref, reactive } from 'vue'
export function useForm(initialValues = {}, validators = {}) {
const form = reactive({ ...initialValues })
const errors = reactive({})
const touched = reactive({})
const validate = (field) => {
if (validators[field]) {
const error = validators[field](form[field])
errors[field] = error
return !error
}
return true
}
const validateAll = () => {
return Object.keys(validators).every(validate)
}
const setField = (field, value) => {
form[field] = value
touched[field] = true
}
return {
form,
errors,
touched,
validate,
validateAll,
setField
}
}
2. 响应式路由
// composables/useRoute.js
import { ref, computed } from 'vue'
import { useRoute as useVueRoute } from 'vue-router'
export function useRoute() {
const route = useVueRoute()
const queryParams = computed(() => route.query)
const params = computed(() => route.params)
const fullPath = computed(() => route.fullPath)
return {
route,
queryParams,
params,
fullPath
}
}
最佳实践
1. 逻辑组织
将相关的逻辑组织在一起:
export default {
setup() {
// 状态定义
const count = ref(0)
const loading = ref(false)
// 计算属性
const doubleCount = computed(() => count.value * 2)
// 方法
const increment = () => count.value++
// 生命周期
onMounted(() => {
console.log('mounted')
})
// 返回
return {
count,
doubleCount,
increment
}
}
}
2. 使用 <script setup>
Vue 3.2+ 引入了 <script setup>,让 Composition API 更加简洁:
<script setup>
import { ref, computed, onMounted } from 'vue'
const count = ref(0)
const doubleCount = computed(() => count.value * 2)
const increment = () => count.value++
onMounted(() => {
console.log('mounted')
})
</script>
<template>
<div>
<p>{{ count }}</p>
<p>{{ doubleCount }}</p>
<button @click="increment">增加</button>
</div>
</template>
3. 类型推导
Composition API 对 TypeScript 支持更好:
import { ref } from 'vue'
// 类型自动推导
const count = ref(0) // Ref<number>
const user = ref({ name: '张三', age: 25 }) // Ref<{ name: string, age: number }>
// 显式类型
const count2 = ref<number>(0)
interface User {
name: string
age: number
}
const user2 = ref<User>({ name: '张三', age: 25 })
总结
Composition API 为 Vue 3 带来了:
- 更好的逻辑组织 - 相关代码放在一起,而不是分散在多个选项中
- 更强的逻辑复用 - 通过组合式函数实现代码复用
- 更好的 TypeScript 支持 - 类型推导更准确
- 更小的打包体积 - 更好的 tree-shaking
虽然学习曲线稍陡,但对于中大型项目来说,Composition API 是更好的选择。建议从简单的 ref、computed、watch 开始,逐步掌握组合式函数的编写技巧。