Vue 3 Composition API深度解析:从Options到Composition的革命
Vue 3的Composition API是Vue生态系统中最重大的变革之一。它彻底改变了我们组织和复用Vue组件逻辑的方式。本文将深入探讨Composition API的核心概念、优势以及实际应用。
1. Options API的局限性
在Vue 2中,我们使用Options API组织组件逻辑。虽然直观,但随着组件复杂度增加,会出现一些问题:
// Vue 2 Options API示例 export default { data() { return { count: 0, user: null, loading: false } }, computed: { doubleCount() { return this.count * 2 }, userName() { return this.user ? this.user.name : 'Guest' } }, methods: { increment() { this.count++ }, async fetchUser() { this.loading = true try { this.user = await api.getUser() } finally { this.loading = false } } }, mounted() { this.fetchUser() } }
问题:相关逻辑分散在data、computed、methods、lifecycle hooks中,难以维护和复用。
2. Composition API基础
Composition API通过setup函数和响应式API提供了更灵活的逻辑组织方式:
// Vue 3 Composition API基础示例 import { ref, computed, onMounted } from 'vue' export default { setup() { // 响应式状态 const count = ref(0) const user = ref(null) const loading = ref(false) // 计算属性 const doubleCount = computed(() => count.value * 2) const userName = computed(() => user.value ? user.value.name : 'Guest') // 方法 const increment = () => { count.value++ } const fetchUser = async () => { loading.value = true try { user.value = await api.getUser() } finally { loading.value = false } } // 生命周期钩子 onMounted(() => { fetchUser() }) // 返回模板可用的内容 return { count, user, loading, doubleCount, userName, increment, fetchUser } } }
3. 逻辑复用:Composables
Composition API最强大的特性之一是能够创建可复用的逻辑函数(Composables):
// useCounter.js - 计数器逻辑复用 import { ref, computed } from 'vue' export function useCounter(initialValue = 0) { const count = ref(initialValue) const double = computed(() => count.value * 2) const triple = computed(() => count.value * 3) const increment = () => count.value++ const decrement = () => count.value-- const reset = () => count.value = initialValue return { count, double, triple, increment, decrement, reset } } // useFetch.js - 数据获取逻辑复用 import { ref, onMounted } from 'vue' export function useFetch(url, options = {}) { const data = ref(null) const error = ref(null) const loading = ref(false) const fetchData = async () => { loading.value = true error.value = null try { const response = await fetch(url, options) if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`) } data.value = await response.json() } catch (err) { error.value = err.message } finally { loading.value = false } } // 自动获取(可选) if (options.autoFetch !== false) { onMounted(fetchData) } return { data, error, loading, fetchData, refetch: fetchData } } // 在组件中使用 import { useCounter, useFetch } from './composables' export default { setup() { const { count, increment, double } = useCounter(10) const { data: user, loading, error } = useFetch('/api/user') return { count, increment, double, user, loading, error } } }
4. 响应式系统深度解析
Vue 3的响应式系统基于Proxy,提供了更强大的能力:
import { reactive, ref, watch, watchEffect, computed } from 'vue' // reactive vs ref const state = reactive({ user: { name: 'John', age: 30 }, items: ['item1', 'item2'] }) const count = ref(0) // 深度监听 watch( () => state.user, (newUser, oldUser) => { console.log('User changed:', newUser) }, { deep: true } ) // 立即执行的监听器 watchEffect(() => { console.log('Count changed:', count.value) console.log('User name:', state.user.name) }) // 计算属性依赖追踪 const userInfo = computed(() => { return `${state.user.name} (${state.user.age}) - Count: ${count.value}` }) // 响应式工具函数 import { toRef, toRefs, isRef, unref } from 'vue' // 将reactive属性转换为ref const userNameRef = toRef(state.user, 'name') // 解构reactive对象并保持响应性 const { user, items } = toRefs(state) // 检查是否为ref console.log(isRef(count)) // true console.log(isRef(state.user)) // false // 安全获取值(如果是ref则获取.value,否则返回原值) const safeValue = unref(count)
5. 高级模式与最佳实践
5.1 状态管理模式
// 使用provide/inject进行状态共享 import { provide, inject, reactive } from 'vue' // 创建全局状态 const createAppState = () => { const state = reactive({ user: null, theme: 'light', notifications: [] }) const setUser = (user) => { state.user = user } const toggleTheme = () => { state.theme = state.theme === 'light' ? 'dark' : 'light' } const addNotification = (message) => { state.notifications.push({ id: Date.now(), message, read: false }) } return { state, setUser, toggleTheme, addNotification } } // 在根组件提供状态 const App = { setup() { const appState = createAppState() provide('appState', appState) return { appState } } } // 在子组件中使用 const ChildComponent = { setup() { const appState = inject('appState') return { appState } } }
5.2 类型安全与TypeScript
// 使用TypeScript增强类型安全 import { defineComponent, ref, computed } from 'vue' interface User { id: number name: string email: string } interface CounterState { count: number double: number increment: () => void reset: () => void } // 类型化的composable function useCounter(initialValue: number = 0): CounterState { const count = ref(initialValue) const double = computed(() => count.value * 2) const increment = () => { count.value++ } const reset = () => { count.value = initialValue } return { count: count.value, double: double.value, increment, reset } } // 定义组件 export default defineComponent({ name: 'UserProfile', props: { userId: { type: Number, required: true } }, setup(props) { const user = ref(null) const { count, increment } = useCounter() // props是响应式的 console.log('User ID:', props.userId) return { user, count, increment } } })
5.3 性能优化技巧
// 1. 使用shallowRef/shallowReactive避免不必要的深度响应 import { shallowRef, shallowReactive } from 'vue' const largeObject = shallowRef({ /* 大型对象 */ }) const config = shallowReactive({ /* 配置对象,内部属性不响应 */ }) // 2. 使用markRaw标记非响应式对象 import { markRaw } from 'vue' const staticData = markRaw({ // 这个对象永远不会被转换为响应式 constants: { PI: 3.14159, VERSION: '1.0.0' } }) // 3. 使用customRef实现自定义响应式逻辑 import { customRef } from 'vue' function useDebouncedRef(value, delay = 200) { let timeout return customRef((track, trigger) => { return { get() { track() return value }, set(newValue) { clearTimeout(timeout) timeout = setTimeout(() => { value = newValue trigger() }, delay) } } }) } // 使用防抖ref const searchQuery = useDebouncedRef('', 300) // 4. 使用readonly保护状态 import { readonly } from 'vue' const protectedState = readonly(reactive({ // 这个状态只能读取,不能修改 settings: { theme: 'dark' } }))
6. 实战:构建一个完整的Todo应用
// useTodos.js - Todo逻辑composable import { ref, computed } from 'vue' export function useTodos() { const todos = ref([]) const filter = ref('all') // 添加todo const addTodo = (text) => { if (!text.trim()) return todos.value.push({ id: Date.now(), text: text.trim(), completed: false, createdAt: new Date() }) } // 删除todo const removeTodo = (id) => { const index = todos.value.findIndex(todo => todo.id === id) if (index !== -1) { todos.value.splice(index, 1) } } // 切换完成状态 const toggleTodo = (id) => { const todo = todos.value.find(todo => todo.id === id) if (todo) { todo.completed = !todo.completed } } // 过滤后的todos const filteredTodos = computed(() => { switch (filter.value) { case 'active': return todos.value.filter(todo => !todo.completed) case 'completed': return todos.value.filter(todo => todo.completed) default: return todos.value } }) // 统计 const stats = computed(() => { const total = todos.value.length const completed = todos.value.filter(todo => todo.completed).length const active = total - completed return { total, completed, active, completionRate: total > 0 ? (completed / total * 100).toFixed(1) : 0 } }) // 清空已完成 const clearCompleted = () => { todos.value = todos.value.filter(todo => !todo.completed) } return { todos, filter, addTodo, removeTodo, toggleTodo, filteredTodos, stats, clearCompleted } } // TodoApp.vue - 主组件 import { useTodos } from './useTodos' export default { setup() { const { todos, filter, addTodo, removeTodo, toggleTodo, filteredTodos, stats, clearCompleted } = useTodos() const newTodoText = ref('') const handleAddTodo = () => { addTodo(newTodoText.value) newTodoText.value = '' } return { todos, filter, newTodoText, filteredTodos, stats, handleAddTodo, removeTodo, toggleTodo, clearCompleted } } }
总结
Vue 3的Composition API带来了革命性的变化:
- 更好的逻辑组织:相关逻辑可以组织在一起,而不是分散在Options中
- 更强的复用性:通过Composables实现逻辑的高度复用
- 更好的TypeScript支持:完整的类型推断和类型安全
- 更灵活的响应式系统:基于Proxy的响应式系统更强大、更高效
- 更好的性能:Tree-shaking支持、更小的包体积
虽然Composition API的学习曲线比Options API更陡峭,但它为构建大型、复杂的Vue应用提供了更好的工具和模式。对于新项目,建议直接使用Composition API;对于现有项目,可以逐步迁移,两者可以共存。
掌握Composition API不仅能让你的Vue代码更加模块化和可维护,还能让你更好地理解Vue的响应式原理和设计哲学。这是每个Vue开发者都应该掌握的技能。
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END




暂无评论内容