Vue 3 Composition API实战指南:从基础语法到复杂组件状态管理

Oscar290
Oscar290 2026-01-31T07:01:00+08:00
0 0 1

前言

Vue 3 的发布带来了革命性的变化,其中最引人注目的就是 Composition API 的引入。这一新特性为开发者提供了更加灵活和强大的组件开发方式,特别是在处理复杂组件逻辑时表现尤为突出。本文将深入探讨 Vue 3 Composition API 的核心概念、使用方法以及最佳实践,帮助开发者从基础语法逐步掌握这一现代化的开发模式。

什么是 Composition API

理念与优势

Composition API 是 Vue 3 中引入的一种新的组件逻辑组织方式,它允许我们以函数的形式组织和重用组件逻辑。相比于 Vue 2 中的选项式 API(Options API),Composition API 具有以下显著优势:

  • 更好的逻辑复用:通过组合函数(composables)实现逻辑复用
  • 更灵活的代码组织:按功能而不是按选项类型组织代码
  • 更强的类型支持:与 TypeScript 配合使用时提供更好的类型推断
  • 更清晰的组件结构:将相关的逻辑集中在一起

与 Options API 的对比

让我们通过一个简单的例子来理解两者的区别:

// Vue 2 Options API
export default {
  data() {
    return {
      count: 0,
      message: 'Hello'
    }
  },
  methods: {
    increment() {
      this.count++
    },
    decrement() {
      this.count--
    }
  },
  computed: {
    doubledCount() {
      return this.count * 2
    }
  }
}

// Vue 3 Composition API
import { ref, computed } from 'vue'

export default {
  setup() {
    const count = ref(0)
    const message = ref('Hello')
    
    const increment = () => {
      count.value++
    }
    
    const decrement = () => {
      count.value--
    }
    
    const doubledCount = computed(() => count.value * 2)
    
    return {
      count,
      message,
      increment,
      decrement,
      doubledCount
    }
  }
}

响应式 API 核心概念

ref 和 reactive 的基本使用

在 Composition API 中,响应式数据的创建主要通过 refreactive 两个核心函数来实现。

ref 的使用

ref 用于创建响应式的数据引用,它会自动包装基础类型为对象:

import { ref } from 'vue'

// 创建基础类型的响应式变量
const count = ref(0)
const name = ref('Vue')

// 访问和修改值
console.log(count.value) // 0
count.value = 10
console.log(count.value) // 10

// 在模板中使用
// <template>
//   <p>{{ count }}</p>
// </template>

reactive 的使用

reactive 用于创建响应式对象,适用于复杂数据结构:

import { reactive } from 'vue'

const state = reactive({
  count: 0,
  name: 'Vue',
  user: {
    firstName: 'John',
    lastName: 'Doe'
  }
})

// 修改属性
state.count = 10
state.user.firstName = 'Jane'

// 在模板中使用
// <template>
//   <p>{{ state.count }}</p>
//   <p>{{ state.user.firstName }}</p>
// </template>

toRefs 和 toRef 的区别

当需要将响应式对象的属性解构时,toRefstoRef 提供了不同的解决方案:

import { reactive, toRefs } from 'vue'

const state = reactive({
  count: 0,
  name: 'Vue'
})

// 使用 toRefs 解构
const { count, name } = toRefs(state)
// 这样解构出来的 count 和 name 仍然是响应式的

// 如果直接解构,会失去响应性
const { count, name } = state // 这样解构后不再是响应式的

生命周期钩子的使用

setup 函数中的生命周期

在 Composition API 中,我们通过 setup 函数来组织组件逻辑,并使用特定的函数来处理生命周期:

import { onMounted, onUpdated, onUnmounted } from 'vue'

export default {
  setup() {
    // 组件挂载时执行
    onMounted(() => {
      console.log('组件已挂载')
    })
    
    // 组件更新时执行
    onUpdated(() => {
      console.log('组件已更新')
    })
    
    // 组件卸载前执行
    onUnmounted(() => {
      console.log('组件即将卸载')
    })
    
    return {
      // 返回的数据和方法
    }
  }
}

完整的生命周期示例

import { ref, onMounted, onUpdated, onUnmounted, watch } from 'vue'

export default {
  setup() {
    const count = ref(0)
    const message = ref('Hello')
    
    // 挂载时执行
    onMounted(() => {
      console.log('组件已挂载')
      // 可以在这里执行 DOM 操作
    })
    
    // 更新时执行
    onUpdated(() => {
      console.log('组件已更新')
    })
    
    // 卸载前执行
    onUnmounted(() => {
      console.log('组件即将卸载')
    })
    
    // 监听响应式数据变化
    watch(count, (newValue, oldValue) => {
      console.log(`count 从 ${oldValue} 变为 ${newValue}`)
    })
    
    const increment = () => {
      count.value++
    }
    
    return {
      count,
      message,
      increment
    }
  }
}

组合函数(Composables)最佳实践

创建可复用的组合函数

组合函数是 Composition API 的核心概念之一,它允许我们将组件逻辑封装成可复用的函数:

// composables/useCounter.js
import { ref, computed } from 'vue'

export function useCounter(initialValue = 0) {
  const count = ref(initialValue)
  
  const increment = () => {
    count.value++
  }
  
  const decrement = () => {
    count.value--
  }
  
  const reset = () => {
    count.value = initialValue
  }
  
  const doubled = computed(() => count.value * 2)
  
  return {
    count,
    increment,
    decrement,
    reset,
    doubled
  }
}

// composables/useFetch.js
import { ref, watch } from 'vue'

export function useFetch(url) {
  const data = ref(null)
  const loading = ref(false)
  const error = ref(null)
  
  const fetchData = async () => {
    loading.value = true
    error.value = null
    
    try {
      const response = await fetch(url)
      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
    }
  }
  
  // 立即执行一次
  fetchData()
  
  // 监听 URL 变化,重新获取数据
  watch(url, fetchData)
  
  return {
    data,
    loading,
    error,
    refetch: fetchData
  }
}

使用组合函数

import { useCounter } from '@/composables/useCounter'
import { useFetch } from '@/composables/useFetch'

export default {
  setup() {
    // 使用计数器组合函数
    const { count, increment, decrement, doubled } = useCounter(0)
    
    // 使用数据获取组合函数
    const { data, loading, error, refetch } = useFetch('/api/users')
    
    return {
      count,
      increment,
      decrement,
      doubled,
      data,
      loading,
      error,
      refetch
    }
  }
}

组件通信与数据传递

父子组件通信

在 Composition API 中,父子组件通信依然遵循 Vue 的标准模式:

// 父组件
import { ref } from 'vue'

export default {
  setup() {
    const parentMessage = ref('来自父组件的消息')
    
    const handleChildEvent = (message) => {
      console.log('收到子组件消息:', message)
    }
    
    return {
      parentMessage,
      handleChildEvent
    }
  }
}

// 子组件
export default {
  props: ['message'],
  emits: ['child-event'],
  setup(props, { emit }) {
    const childMessage = ref('来自子组件的消息')
    
    const sendToParent = () => {
      emit('child-event', childMessage.value)
    }
    
    return {
      childMessage,
      sendToParent
    }
  }
}

使用 provide 和 inject 实现跨层级通信

// 父组件
import { provide, ref } from 'vue'

export default {
  setup() {
    const theme = ref('dark')
    const user = ref({ name: 'John', role: 'admin' })
    
    // 提供数据给后代组件
    provide('theme', theme)
    provide('user', user)
    
    return {
      theme,
      user
    }
  }
}

// 子组件
import { inject } from 'vue'

export default {
  setup() {
    // 注入提供的数据
    const theme = inject('theme')
    const user = inject('user')
    
    return {
      theme,
      user
    }
  }
}

复杂状态管理实战

创建全局状态管理器

// stores/useGlobalStore.js
import { reactive, readonly } from 'vue'

// 创建全局状态
const state = reactive({
  user: null,
  isLoggedIn: false,
  theme: 'light',
  notifications: []
})

// 提供状态访问方法
export function useGlobalStore() {
  const getUser = () => state.user
  const getIsLoggedIn = () => state.isLoggedIn
  const getTheme = () => state.theme
  const getNotifications = () => state.notifications
  
  // 提供状态更新方法
  const setUser = (user) => {
    state.user = user
    state.isLoggedIn = !!user
  }
  
  const setTheme = (theme) => {
    state.theme = theme
  }
  
  const addNotification = (notification) => {
    state.notifications.push({
      id: Date.now(),
      ...notification,
      timestamp: new Date()
    })
  }
  
  const removeNotification = (id) => {
    const index = state.notifications.findIndex(n => n.id === id)
    if (index > -1) {
      state.notifications.splice(index, 1)
    }
  }
  
  // 返回只读状态和方法
  return {
    state: readonly(state),
    getUser,
    getIsLoggedIn,
    getTheme,
    getNotifications,
    setUser,
    setTheme,
    addNotification,
    removeNotification
  }
}

在组件中使用全局状态

import { useGlobalStore } from '@/stores/useGlobalStore'
import { computed } from 'vue'

export default {
  setup() {
    const { 
      state, 
      getUser, 
      getIsLoggedIn, 
      getTheme, 
      addNotification,
      setUser 
    } = useGlobalStore()
    
    // 计算属性
    const currentUser = computed(() => getUser())
    const isLoggedIn = computed(() => getIsLoggedIn())
    const currentTheme = computed(() => getTheme())
    
    // 处理登录
    const handleLogin = async (credentials) => {
      try {
        // 模拟 API 调用
        const user = await login(credentials)
        setUser(user)
        addNotification({
          type: 'success',
          message: '登录成功'
        })
      } catch (error) {
        addNotification({
          type: 'error',
          message: '登录失败'
        })
      }
    }
    
    return {
      state,
      currentUser,
      isLoggedIn,
      currentTheme,
      handleLogin
    }
  }
}

性能优化与最佳实践

合理使用计算属性和监听器

import { ref, computed, watch, watchEffect } from 'vue'

export default {
  setup() {
    const firstName = ref('John')
    const lastName = ref('Doe')
    const age = ref(25)
    
    // 使用 computed 创建计算属性
    const fullName = computed(() => `${firstName.value} ${lastName.value}`)
    
    // 使用 watch 监听特定数据变化
    watch(firstName, (newVal, oldVal) => {
      console.log(`firstName 从 ${oldVal} 变为 ${newVal}`)
    })
    
    // 使用 watchEffect 自动追踪依赖
    const fullNameWithAge = computed(() => {
      return `${firstName.value} ${lastName.value} (${age.value})`
    })
    
    // 监听多个数据源
    watch([firstName, lastName], ([newFirst, newLast], [oldFirst, oldLast]) => {
      console.log(`姓名从 ${oldFirst} ${oldLast} 变为 ${newFirst} ${newLast}`)
    })
    
    return {
      firstName,
      lastName,
      age,
      fullName,
      fullNameWithAge
    }
  }
}

组件性能优化技巧

import { ref, computed, onMounted, onUnmounted } from 'vue'

export default {
  setup() {
    const data = ref([])
    const loading = ref(false)
    
    // 使用懒加载避免不必要的计算
    const expensiveComputed = computed(() => {
      // 只有当需要时才执行昂贵的计算
      return data.value.map(item => ({
        ...item,
        processed: item.name.toUpperCase()
      }))
    })
    
    // 使用防抖优化高频事件
    const debouncedSearch = (searchTerm) => {
      // 防抖实现
      clearTimeout(searchTimeout)
      searchTimeout = setTimeout(() => {
        fetchData(searchTerm)
      }, 300)
    }
    
    let searchTimeout
    
    // 合理的生命周期管理
    onMounted(() => {
      // 组件挂载时执行初始化操作
      loadData()
    })
    
    onUnmounted(() => {
      // 清理定时器等资源
      if (searchTimeout) {
        clearTimeout(searchTimeout)
      }
    })
    
    return {
      data,
      loading,
      expensiveComputed
    }
  }
}

实战案例:构建一个完整的用户管理系统

组件结构设计

让我们通过一个完整的用户管理系统来演示 Composition API 的实际应用:

// components/UserList.vue
import { ref, computed, onMounted } from 'vue'
import { useUserStore } from '@/stores/useUserStore'

export default {
  name: 'UserList',
  setup() {
    const { 
      users, 
      loading, 
      error,
      fetchUsers,
      deleteUser,
      addUser
    } = useUserStore()
    
    const searchTerm = ref('')
    const showAddForm = ref(false)
    const newUserForm = ref({
      name: '',
      email: '',
      role: 'user'
    })
    
    // 过滤用户列表
    const filteredUsers = computed(() => {
      if (!searchTerm.value) return users.value
      
      return users.value.filter(user => 
        user.name.toLowerCase().includes(searchTerm.value.toLowerCase()) ||
        user.email.toLowerCase().includes(searchTerm.value.toLowerCase())
      )
    })
    
    // 处理删除用户
    const handleDeleteUser = async (userId) => {
      if (confirm('确定要删除这个用户吗?')) {
        try {
          await deleteUser(userId)
          // 可以添加删除成功的通知
        } catch (error) {
          console.error('删除用户失败:', error)
        }
      }
    }
    
    // 处理添加新用户
    const handleAddUser = async () => {
      if (!newUserForm.value.name || !newUserForm.value.email) {
        alert('请填写必填字段')
        return
      }
      
      try {
        await addUser(newUserForm.value)
        newUserForm.value = { name: '', email: '', role: 'user' }
        showAddForm.value = false
      } catch (error) {
        console.error('添加用户失败:', error)
      }
    }
    
    // 初始化数据
    onMounted(() => {
      fetchUsers()
    })
    
    return {
      users: filteredUsers,
      loading,
      error,
      searchTerm,
      showAddForm,
      newUserForm,
      handleDeleteUser,
      handleAddUser
    }
  }
}

状态管理实现

// stores/useUserStore.js
import { reactive, readonly } from 'vue'

const state = reactive({
  users: [],
  loading: false,
  error: null
})

// API 调用函数(模拟)
const fetchUsersFromAPI = async () => {
  // 模拟 API 调用
  return new Promise(resolve => {
    setTimeout(() => {
      resolve([
        { id: 1, name: '张三', email: 'zhangsan@example.com', role: 'admin' },
        { id: 2, name: '李四', email: 'lisi@example.com', role: 'user' }
      ])
    }, 1000)
  })
}

const deleteUserFromAPI = async (id) => {
  return new Promise(resolve => {
    setTimeout(() => resolve({ success: true }), 500)
  })
}

const addUserToAPI = async (userData) => {
  return new Promise(resolve => {
    setTimeout(() => resolve({ id: Date.now(), ...userData }), 500)
  })
}

export function useUserStore() {
  const fetchUsers = async () => {
    state.loading = true
    state.error = null
    
    try {
      const users = await fetchUsersFromAPI()
      state.users = users
    } catch (error) {
      state.error = error.message
    } finally {
      state.loading = false
    }
  }
  
  const deleteUser = async (id) => {
    state.loading = true
    
    try {
      await deleteUserFromAPI(id)
      state.users = state.users.filter(user => user.id !== id)
    } catch (error) {
      state.error = error.message
    } finally {
      state.loading = false
    }
  }
  
  const addUser = async (userData) => {
    state.loading = true
    
    try {
      const newUser = await addUserToAPI(userData)
      state.users.push(newUser)
    } catch (error) {
      state.error = error.message
    } finally {
      state.loading = false
    }
  }
  
  return {
    state: readonly(state),
    fetchUsers,
    deleteUser,
    addUser
  }
}

使用组合函数优化

// composables/useForm.js
import { ref, reactive } from 'vue'

export function useForm(initialValues = {}) {
  const form = reactive({ ...initialValues })
  const errors = ref({})
  const isSubmitting = ref(false)
  
  const validate = (rules) => {
    const newErrors = {}
    
    Object.keys(rules).forEach(field => {
      const rule = rules[field]
      const value = form[field]
      
      if (rule.required && !value) {
        newErrors[field] = `${field} 是必填项`
      } else if (rule.pattern && !rule.pattern.test(value)) {
        newErrors[field] = rule.message || '格式不正确'
      }
    })
    
    errors.value = newErrors
    return Object.keys(newErrors).length === 0
  }
  
  const reset = () => {
    Object.keys(form).forEach(key => {
      form[key] = initialValues[key] || ''
    })
    errors.value = {}
  }
  
  const submit = async (submitFn) => {
    if (!validate()) return false
    
    isSubmitting.value = true
    
    try {
      const result = await submitFn(form)
      reset()
      return result
    } catch (error) {
      console.error('提交失败:', error)
      return false
    } finally {
      isSubmitting.value = false
    }
  }
  
  return {
    form,
    errors,
    isSubmitting,
    validate,
    reset,
    submit
  }
}

总结与展望

Vue 3 Composition API 的引入为前端开发带来了革命性的变化。通过本文的详细介绍,我们看到了:

  1. 响应式系统的强大:ref 和 reactive 提供了灵活的数据响应能力
  2. 生命周期管理的清晰性:通过特定的钩子函数更好地控制组件行为
  3. 逻辑复用的便利性:组合函数让代码复用变得更加简单和优雅
  4. 状态管理的现代化:结合组合函数可以构建出更加可维护的状态管理系统

在实际开发中,建议:

  • 从简单的场景开始使用 Composition API
  • 合理设计组合函数,保持其职责单一
  • 注意性能优化,避免不必要的计算和监听
  • 结合 TypeScript 使用以获得更好的类型安全

随着 Vue 生态系统的不断发展,Composition API 必将成为现代 Vue 开发的标准实践。掌握这一技术不仅能够提高开发效率,还能让代码更加清晰、可维护和可测试。

通过本文的学习和实践,相信读者已经对 Vue 3 Composition API 有了深入的理解,并能够在实际项目中灵活运用这些技术来构建高质量的现代化应用。

相关推荐
广告位招租

相似文章

    评论 (0)

    0/2000