Vue 3 Composition API实战:从基础语法到复杂组件状态管理完整教程

GladIvan
GladIvan 2026-02-01T16:06:00+08:00
0 0 3

前言

Vue 3 的发布带来了革命性的变化,其中最引人注目的就是 Composition API 的引入。相比 Vue 2 中的 Options API,Composition API 提供了更加灵活和强大的组件状态管理方式。本文将深入探讨 Composition API 的核心概念、使用方法以及在实际项目中的最佳实践。

什么是 Composition API

Composition API 是 Vue 3 中引入的一种新的组件状态管理方式,它允许开发者以函数的形式组织和重用组件逻辑。与传统的 Options API 相比,Composition API 更加灵活,能够更好地处理复杂的状态逻辑,特别是在需要在多个组件间共享逻辑时。

Composition API 的核心优势

  1. 更好的逻辑复用:通过组合函数实现逻辑的复用
  2. 更清晰的代码组织:将相关的逻辑集中在一起
  3. 更强的类型支持:与 TypeScript 集成更好
  4. 更灵活的状态管理:可以动态地添加和移除状态

响应式 API 基础

reactive 和 ref 的基本使用

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

import { reactive, ref } from 'vue'

// 使用 ref 创建响应式数据
const count = ref(0)
const name = ref('Vue')

// 使用 reactive 创建响应式对象
const state = reactive({
  count: 0,
  name: 'Vue',
  user: {
    age: 20,
    email: 'vue@example.com'
  }
})

// 在模板中使用
console.log(count.value) // 0
console.log(state.count) // 0

// 修改值
count.value = 10
state.count = 5

ref 与 reactive 的区别

import { ref, reactive } from 'vue'

// ref 创建的是响应式引用
const count = ref(0)
const doubleCount = computed(() => count.value * 2)

// reactive 创建的是响应式对象
const state = reactive({
  count: 0,
  doubleCount: computed(() => state.count * 2)
})

// 在模板中使用时的区别
// ref 需要 .value 访问
// reactive 直接访问属性

响应式数据的解构和展开

import { reactive, toRefs } from 'vue'

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

// 使用 toRefs 解构响应式对象
const { count, name, age } = toRefs(state)

// 这样解构后的变量仍然是响应式的
count.value++ // 状态会更新

计算属性和监听器

computed 的使用

import { ref, computed } from 'vue'

const firstName = ref('John')
const lastName = ref('Doe')

// 基本计算属性
const fullName = computed(() => {
  return `${firstName.value} ${lastName.value}`
})

// 带有 getter 和 setter 的计算属性
const reversedName = computed({
  get: () => {
    return firstName.value.split('').reverse().join('')
  },
  set: (value) => {
    firstName.value = value.split('').reverse().join('')
  }
})

watch 的使用

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

const count = ref(0)
const name = ref('Vue')

// 基本监听器
watch(count, (newVal, oldVal) => {
  console.log(`count 从 ${oldVal} 变为 ${newVal}`)
})

// 监听多个源
watch([count, name], ([newCount, newName], [oldCount, oldName]) => {
  console.log(`count: ${oldCount} -> ${newCount}, name: ${oldName} -> ${newName}`)
})

// watchEffect 会自动追踪依赖
watchEffect(() => {
  console.log(`当前 count 值为: ${count.value}`)
})

// 停止监听器
const stop = watch(count, (newVal) => {
  console.log(newVal)
})
stop() // 停止监听

生命周期钩子

Composition API 中的生命周期钩子

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

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

在 setup 中使用生命周期钩子

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

export default {
  setup() {
    const count = ref(0)
    let timer = null
    
    onMounted(() => {
      // 组件挂载后启动定时器
      timer = setInterval(() => {
        count.value++
      }, 1000)
    })
    
    onUnmounted(() => {
      // 组件卸载前清除定时器
      if (timer) {
        clearInterval(timer)
      }
    })
    
    return {
      count
    }
  }
}

组件通信

父子组件通信

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

export default {
  setup() {
    const message = ref('Hello from parent')
    
    const sendMessageToChild = (msg) => {
      console.log(`父组件收到消息: ${msg}`)
    }
    
    return {
      message,
      sendMessageToChild
    }
  }
}

// 子组件
import { defineProps, defineEmits } from 'vue'

export default {
  props: {
    message: String
  },
  emits: ['update-message'],
  setup(props, { emit }) {
    const updateMessage = () => {
      emit('update-message', 'Hello from child')
    }
    
    return {
      updateMessage
    }
  }
}

兄弟组件通信

// 使用 provide 和 inject 实现跨层级通信
import { provide, inject, ref } from 'vue'

// 祖先组件
export default {
  setup() {
    const sharedData = ref('Shared data')
    
    provide('sharedData', sharedData)
    provide('updateSharedData', (newData) => {
      sharedData.value = newData
    })
    
    return {
      sharedData
    }
  }
}

// 后代组件
export default {
  setup() {
    const sharedData = inject('sharedData')
    const updateSharedData = inject('updateSharedData')
    
    const changeData = () => {
      updateSharedData('New data from child')
    }
    
    return {
      sharedData,
      changeData
    }
  }
}

组合函数(Composable Functions)

创建可复用的逻辑

// 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 doubleCount = computed(() => count.value * 2)
  
  return {
    count,
    increment,
    decrement,
    reset,
    doubleCount
  }
}

// 在组件中使用
import { useCounter } from '@/composables/useCounter'

export default {
  setup() {
    const { count, increment, decrement, reset, doubleCount } = useCounter(10)
    
    return {
      count,
      increment,
      decrement,
      reset,
      doubleCount
    }
  }
}

复杂的组合函数示例

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

export function useApi(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
    }
  }
  
  const refresh = () => fetchData()
  
  const hasData = computed(() => data.value !== null)
  
  return {
    data,
    loading,
    error,
    fetchData,
    refresh,
    hasData
  }
}

// 使用示例
export default {
  setup() {
    const { data, loading, error, fetchData } = useApi('/api/users')
    
    fetchData()
    
    return {
      data,
      loading,
      error,
      fetchData
    }
  }
}

状态管理最佳实践

复杂组件的状态管理

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

export function useForm(initialData = {}) {
  const form = reactive({ ...initialData })
  const errors = reactive({})
  const isSubmitting = ref(false)
  
  const isValid = computed(() => {
    return Object.keys(errors).length === 0
  })
  
  const validateField = (field, value) => {
    // 简单的验证逻辑
    if (!value) {
      errors[field] = `${field} 是必填项`
      return false
    }
    
    delete errors[field]
    return true
  }
  
  const validateForm = () => {
    Object.keys(form).forEach(field => {
      validateField(field, form[field])
    })
    return isValid.value
  }
  
  const submit = async (submitFn) => {
    if (!validateForm()) {
      return false
    }
    
    isSubmitting.value = true
    
    try {
      await submitFn(form)
      return true
    } catch (err) {
      console.error('提交失败:', err)
      return false
    } finally {
      isSubmitting.value = false
    }
  }
  
  const reset = () => {
    Object.keys(form).forEach(key => {
      form[key] = initialData[key] || ''
    })
    Object.keys(errors).forEach(key => {
      delete errors[key]
    })
  }
  
  return {
    form,
    errors,
    isSubmitting,
    isValid,
    validateField,
    validateForm,
    submit,
    reset
  }
}

// 在组件中使用
export default {
  setup() {
    const { 
      form, 
      errors, 
      isSubmitting, 
      isValid, 
      validateField, 
      submit, 
      reset 
    } = useForm({
      name: '',
      email: '',
      message: ''
    })
    
    const handleInput = (field, value) => {
      form[field] = value
      validateField(field, value)
    }
    
    const handleSubmit = async (formData) => {
      // 处理表单提交
      console.log('提交数据:', formData)
      // 这里可以调用 API
    }
    
    return {
      form,
      errors,
      isSubmitting,
      isValid,
      handleInput,
      submit: (e) => submit(handleSubmit),
      reset
    }
  }
}

状态持久化

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

export function useLocalStorage(key, defaultValue) {
  const storedValue = localStorage.getItem(key)
  const value = ref(storedValue ? JSON.parse(storedValue) : defaultValue)
  
  watch(value, (newValue) => {
    localStorage.setItem(key, JSON.stringify(newValue))
  }, { deep: true })
  
  return value
}

// 使用示例
export default {
  setup() {
    const userPreferences = useLocalStorage('userPreferences', {
      theme: 'light',
      language: 'zh-CN'
    })
    
    const toggleTheme = () => {
      userPreferences.value.theme = 
        userPreferences.value.theme === 'light' ? 'dark' : 'light'
    }
    
    return {
      userPreferences,
      toggleTheme
    }
  }
}

性能优化技巧

避免不必要的计算

import { computed, shallowRef } from 'vue'

// 对于大型对象,使用 shallowRef 可以避免深度监听
const largeObject = shallowRef({
  data: new Array(10000).fill(0),
  metadata: {}
})

// 使用 computed 缓存计算结果
const expensiveCalculation = computed(() => {
  // 复杂的计算逻辑
  return someExpensiveOperation(largeObject.value.data)
})

条件渲染和动态组件

import { ref, defineAsyncComponent } from 'vue'

export default {
  setup() {
    const currentComponent = ref('ComponentA')
    
    // 异步加载组件
    const AsyncComponent = defineAsyncComponent(() => 
      import('./components/AsyncComponent.vue')
    )
    
    return {
      currentComponent,
      AsyncComponent
    }
  }
}

实际项目应用示例

完整的用户管理系统

<template>
  <div class="user-management">
    <h2>用户管理</h2>
    
    <!-- 用户列表 -->
    <div class="user-list">
      <div v-for="user in users" :key="user.id" class="user-card">
        <h3>{{ user.name }}</h3>
        <p>{{ user.email }}</p>
        <button @click="deleteUser(user.id)">删除</button>
      </div>
    </div>
    
    <!-- 加载状态 -->
    <div v-if="loading">加载中...</div>
    
    <!-- 错误处理 -->
    <div v-if="error" class="error">{{ error }}</div>
    
    <!-- 分页 -->
    <div class="pagination">
      <button @click="prevPage" :disabled="currentPage <= 1">上一页</button>
      <span>第 {{ currentPage }} 页</span>
      <button @click="nextPage" :disabled="currentPage >= totalPages">下一页</button>
    </div>
  </div>
</template>

<script>
import { ref, computed, onMounted } from 'vue'
import { useApi } from '@/composables/useApi'

export default {
  name: 'UserManagement',
  setup() {
    const currentPage = ref(1)
    const pageSize = ref(10)
    
    const { data: users, loading, error, fetchData } = useApi('/api/users')
    
    const totalPages = computed(() => {
      if (!users.value) return 0
      return Math.ceil(users.value.length / pageSize.value)
    })
    
    const paginatedUsers = computed(() => {
      if (!users.value) return []
      const start = (currentPage.value - 1) * pageSize.value
      return users.value.slice(start, start + pageSize.value)
    })
    
    const nextPage = () => {
      if (currentPage.value < totalPages.value) {
        currentPage.value++
      }
    }
    
    const prevPage = () => {
      if (currentPage.value > 1) {
        currentPage.value--
      }
    }
    
    const deleteUser = async (userId) => {
      try {
        await fetch(`/api/users/${userId}`, { method: 'DELETE' })
        // 重新获取用户列表
        fetchData()
      } catch (err) {
        console.error('删除用户失败:', err)
      }
    }
    
    onMounted(() => {
      fetchData()
    })
    
    return {
      users: paginatedUsers,
      loading,
      error,
      currentPage,
      totalPages,
      nextPage,
      prevPage,
      deleteUser
    }
  }
}
</script>

<style scoped>
.user-management {
  padding: 20px;
}

.user-card {
  border: 1px solid #ccc;
  margin: 10px 0;
  padding: 15px;
  border-radius: 5px;
}

.pagination {
  text-align: center;
  margin-top: 20px;
}

.error {
  color: red;
  padding: 10px;
  background-color: #ffebee;
  border-radius: 5px;
}
</style>

总结

Vue 3 的 Composition API 为前端开发带来了更加灵活和强大的组件状态管理方式。通过本文的介绍,我们了解了:

  1. 基础概念:理解了 refreactivecomputedwatch 等核心 API
  2. 生命周期管理:掌握了如何在 Composition API 中使用生命周期钩子
  3. 组件通信:学习了父子组件、兄弟组件以及跨层级组件的通信方式
  4. 组合函数:通过自定义组合函数实现逻辑复用
  5. 最佳实践:掌握了状态管理、性能优化等实际应用技巧

Composition API 的引入让 Vue 3 在处理复杂组件逻辑时更加得心应手,特别是在需要在多个组件间共享和重用逻辑的场景下表现尤为突出。随着项目的复杂度增加,Composition API 的优势会更加明显。

建议开发者在项目中逐步采用 Composition API,特别是在重构现有项目或开发新功能时,可以先从简单的组合函数开始,逐步深入理解其强大功能。同时,结合 TypeScript 使用 Composition API 能够获得更好的类型安全和开发体验。

通过持续的实践和学习,相信每位开发者都能熟练掌握 Vue 3 Composition API,并将其运用到实际项目中,构建出更加高效、可维护的前端应用。

相关推荐
广告位招租

相似文章

    评论 (0)

    0/2000