目录
一、为什么需要 Composition API?
1.1 Options API 的局限性
假设你开发一个包含以下功能的复杂组件:
-
用户身份验证
-
实时数据图表
-
表单验证
-
WebSocket 连接
使用 Options API 时代码组织方式:
export default {
data() { /* 混杂各种数据 */ },
methods: { /* 交织不同的功能逻辑 */ },
computed: { /* 分散的计算属性 */ },
mounted() { /* 难以追踪的生命周期 */ }
}
痛点:相关逻辑分散在不同选项中,类似把乐高积木的所有零件混在一个盒子里,难以维护和复用。
二、Composition API 核心思想
2.1 组合式编程哲学
将组件逻辑视为可组合的函数单元,类似按功能分类的乐高积木套装,暂无视图
2.2 核心函数一览
函数 | 作用 | 类比说明 |
---|---|---|
ref | 创建响应式基本类型数据 | 基础积木块 |
reactive | 创建响应式对象 | 预制结构件 |
computed | 创建计算属性 | 齿轮传动系统 |
watch/watchEffect | 响应数据变化执行副作用 | 传感器装置 |
provide/inject | 跨层级组件通信 | 无线信号传输器 |
三、手把手构建 Composition 组件
3.1 基础计数器示例
<template>
<div>
<button @click="decrement">-</button>
<span>{{ count }}</span>
<button @click="increment">+</button>
<p>Double: {{ doubleCount }}</p>
</div>
</template>
<script>
import { ref, computed } from 'vue'
export default {
setup() {
// 响应式状态
const count = ref(0)
// 计算属性
const doubleCount = computed(() => count.value * 2)
// 方法
const increment = () => count.value++
const decrement = () => count.value--
// 暴露给模板
return { count, doubleCount, increment, decrement }
}
}
</script>
3.2 对比 Options API
// Options API 实现
export default {
data() {
return { count: 0 }
},
computed: {
doubleCount() {
return this.count * 2
}
},
methods: {
increment() { this.count++ },
decrement() { this.count-- }
}
}
优势对比:
-
逻辑集中:所有计数器相关代码在
setup()
中聚合 -
类型推断:更好的 TypeScript 支持
-
复用能力:可轻松提取为独立函数
四、深度掌握响应式系统
4.1 ref vs reactive 选择指南
import { ref, reactive } from 'vue'
// 场景1:基本类型值
const count = ref(0) // 正确
const count = reactive({ value: 0 }) // 冗余
// 场景2:对象
const user = reactive({
name: 'Alice',
profile: {
age: 25,
address: 'New York'
}
})
// 场景3:解构对象
const { name } = toRefs(user) // 保持响应式
黄金法则:
-
基本类型用
ref
-
对象/数组用
reactive
-
需要解构时使用
toRefs
4.2 响应式原理揭秘
const raw = { count: 0 }
const proxy = reactive(raw)
console.log(proxy.count === raw.count) // false
console.log(reactive(proxy) === proxy) // true (相同代理)
响应式流程图:
原始对象 → Proxy 代理 → 触发依赖收集 → 数据变更时通知更新
五、高级模式实战
5.1 自定义 Hook:复用登录逻辑
// hooks/useAuth.js
import { ref } from 'vue'
import axios from 'axios'
export default function useAuth() {
const user = ref(null)
const error = ref(null)
const login = async (email, password) => {
try {
const response = await axios.post('/api/login', { email, password })
user.value = response.data
} catch (err) {
error.value = err.message
}
}
const logout = () => {
user.value = null
}
return { user, error, login, logout }
}
// 在组件中使用
import useAuth from '@/hooks/useAuth'
export default {
setup() {
const { user, error, login } = useAuth()
return { user, error, login }
}
}
5.2 组合式表单验证
// hooks/useFormValidation.js
import { ref, computed } from 'vue'
export default function useFormValidation() {
const email = ref('')
const password =ref('')
const emailRules = [
v => !!v || 'Email is required',
v => /.+@.+\..+/.test(v) || 'Email must be valid'
]
const passwordRules = [
v => !!v || 'Password is required',
v => v.length >= 6 || 'Min 6 characters'
]
const errors = computed(() => ({
email: emailRules.map(rule => rule(email.value)).find(error => error),
password: passwordRules.map(rule => rule(password.value)).find(error => error)
}))
const isValid = computed(() =>
!Object.values(errors.value).some(Boolean)
)
return { email, password, errors, isValid }
}
六、与 Options API 混合使用
6.1 渐进式迁移策略
export default {
setup() {
// Composition API 逻辑
const count = ref(0)
return { count }
},
data() {
// Options API 数据
return { message: 'Hello' }
},
methods: {
// 访问 Composition 状态
showCount() {
console.log(this.count)
}
}
}
6.2 生命周期对应关系
Options API | Composition API |
---|---|
beforeCreate | 不需要 (在 setup() 前执行) |
created | 不需要 (在 setup() 前执行) |
beforeMount | onBeforeMount |
mounted | onMounted |
beforeUpdate | onBeforeUpdate |
updated | onUpdated |
beforeUnmount | onBeforeUnmount |
unmounted | onUnmounted |
七、企业级最佳实践
7.1 类型安全的 TypeScript 集成
import { defineComponent, ref } from 'vue'
interface User {
id: number
name: string
email: string
}
export default defineComponent({
setup() {
const user = ref<User>({
id: 1,
name: 'Alice',
email: 'alice@example.com'
})
return { user }
}
})
7.2 性能优化技巧
// 1. 使用 shallowRef 避免深层响应
import { shallowRef } from 'vue'
const bigObject = shallowRef({ ... })
// 2. 合理使用 watchEffect 清理
const stop = watchEffect(() => { ... })
onUnmounted(stop)
// 3. 计算属性缓存优化
const sortedList = computed(() =>
[...items.value].sort((a,b) => a.price - b.price)
)
7.3 状态共享模式
// store/useCart.js
import { reactive } from 'vue'
const state = reactive({
items: [],
total: 0
})
export function useCart() {
const addItem = (item) => {
state.items.push(item)
state.total += item.price
}
return { ...toRefs(state), addItem }
}
// 所有组件共享同一状态
八、常见问题解决方案
问题1:响应式丢失
错误示例:
const { x, y } = reactive({ x: 1, y: 2 }) // 解构后失去响应性
正确方案:
const pos = reactive({ x: 1, y: 2 })
const { x, y } = toRefs(pos) // 保持响应性
问题2:循环引用处理
const obj = reactive({
self: null
})
obj.self = obj // 创建循环引用
// 使用 WeakMap 管理引用
const proxies = new WeakMap()
function getProxy(obj) {
if (proxies.has(obj)) return proxies.get(obj)
const proxy = reactive(obj)
proxies.set(obj, proxy)
return proxy
}
九、实战:电商商品管理系统
<script setup>
import { ref, computed, onMounted } from 'vue'
import useProductSearch from '@/composables/useProductSearch'
// 组合式函数调用
const { searchQuery, filteredProducts } = useProductSearch()
// 独立逻辑
const cart = ref([])
const addToCart = (product) => {
cart.value.push(product)
}
// 生命周期
onMounted(() => {
console.log('Component mounted')
})
</script>
<template>
<div>
<input v-model="searchQuery" placeholder="Search...">
<div class="product-list">
<div v-for="product in filteredProducts" :key="product.id">
<h3>{{ product.name }}</h3>
<button @click="addToCart(product)">Add to Cart</button>
</div>
</div>
<p>Cart Items: {{ cart.length }}</p>
</div>
</template>
总结
Composition API 的核心优势在于逻辑关注点分离和更好的代码复用。通过将组件逻辑拆分为独立的函数单元,开发者可以像搭积木一样构建复杂应用。关键要点:
-
响应式基础:掌握
ref
和reactive
的适用场景 -
逻辑复用:通过自定义 Hook 实现业务逻辑封装
-
类型安全:与 TypeScript 深度集成提升代码质量
-
渐进迁移:支持与 Options API 混合使用
思考题:
在大型项目中,如何组织数百个组合式函数避免混乱?欢迎分享你的架构设计思路!
码字不易,点点赞呗。