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

RedFoot
RedFoot 2026-03-14T23:10:06+08:00
0 0 0

引言

Vue.js 3 的发布带来了革命性的变化,其中最引人注目的就是 Composition API 的引入。这一新特性彻底改变了我们编写 Vue 组件的方式,提供了更灵活、更强大的状态管理和逻辑复用能力。本文将深入探讨 Composition API 的核心概念和使用方法,从基础语法到复杂状态管理方案,为开发者提供一份完整的实战指南。

Vue 3 Composition API 核心概念

什么是 Composition API?

Composition API 是 Vue 3 中引入的一种新的组件开发模式,它允许我们通过组合函数的方式来组织和复用组件逻辑。与传统的 Options API 相比,Composition API 更加灵活,能够更好地处理复杂的业务逻辑和状态管理。

在传统的 Options API 中,我们按照属性、方法、计算属性等将代码分散在不同的选项中。而 Composition API 则允许我们将相关的逻辑组合在一起,使代码更加模块化和可维护。

Composition API 的优势

  1. 更好的逻辑复用:通过组合函数,可以轻松地在多个组件之间共享逻辑
  2. 更灵活的代码组织:按照功能而非类型来组织代码
  3. 更强的类型支持:与 TypeScript 集成更好
  4. 更好的性能:减少了不必要的渲染和计算

基础语法详解

setup 函数

setup 是 Composition API 的入口函数,它在组件实例创建之前执行。在 setup 函数中,我们可以访问组件的 props、context 以及定义响应式数据。

import { ref, reactive } from 'vue'

export default {
  props: {
    title: String
  },
  setup(props, context) {
    // 在这里可以访问 props 和 context
    console.log(props.title)
    console.log(context.emit)
    
    // 定义响应式数据
    const count = ref(0)
    const user = reactive({
      name: 'John',
      age: 30
    })
    
    return {
      count,
      user
    }
  }
}

响应式数据:ref 和 reactive

ref 的使用

ref 用于创建响应式的数据,它会将值包装成一个对象,通过 .value 访问实际值。

import { ref } from 'vue'

export default {
  setup() {
    // 创建基本类型的响应式数据
    const count = ref(0)
    const message = ref('Hello Vue')
    const isActive = ref(true)
    
    // 修改值
    count.value = 10
    message.value = 'Hello World'
    
    return {
      count,
      message,
      isActive
    }
  }
}

reactive 的使用

reactive 用于创建响应式对象,它会将整个对象转换为响应式。

import { reactive } from 'vue'

export default {
  setup() {
    // 创建响应式对象
    const state = reactive({
      count: 0,
      user: {
        name: 'John',
        age: 30
      },
      todos: []
    })
    
    // 修改对象属性
    state.count = 10
    state.user.name = 'Jane'
    
    return {
      state
    }
  }
}

计算属性和侦听器

computed 计算属性

computed 函数用于创建计算属性,它会根据依赖的响应式数据自动计算并缓存结果。

import { ref, computed } from 'vue'

export default {
  setup() {
    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('')
      }
    })
    
    return {
      firstName,
      lastName,
      fullName,
      reversedName
    }
  }
}

watch 侦听器

watch 函数用于监听响应式数据的变化,当数据发生变化时执行相应的回调函数。

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

export default {
  setup() {
    const count = ref(0)
    const name = ref('John')
    const user = reactive({
      age: 30,
      address: {
        city: 'Beijing'
      }
    })
    
    // 监听单个响应式数据
    watch(count, (newVal, oldVal) => {
      console.log(`count changed from ${oldVal} to ${newVal}`)
    })
    
    // 监听多个响应式数据
    watch([count, name], ([newCount, newName], [oldCount, oldName]) => {
      console.log(`count: ${oldCount} -> ${newCount}, name: ${oldName} -> ${newName}`)
    })
    
    // 深度监听对象
    watch(user, (newVal, oldVal) => {
      console.log('user changed:', newVal)
    }, { deep: true })
    
    // 立即执行的侦听器
    watch(count, (newVal) => {
      console.log('immediate watch:', newVal)
    }, { immediate: true })
    
    // watchEffect 会自动追踪其内部使用的响应式数据
    watchEffect(() => {
      console.log(`current count is: ${count.value}`)
    })
    
    return {
      count,
      name,
      user
    }
  }
}

实际应用案例

创建一个用户管理组件

让我们通过一个实际的用户管理组件来展示 Composition API 的强大功能。

<template>
  <div class="user-manager">
    <h2>用户管理系统</h2>
    
    <!-- 用户列表 -->
    <div class="user-list">
      <h3>用户列表</h3>
      <ul>
        <li 
          v-for="user in filteredUsers" 
          :key="user.id"
          @click="selectUser(user)"
          :class="{ selected: selectedUser && selectedUser.id === user.id }"
        >
          {{ user.name }} - {{ user.email }}
        </li>
      </ul>
    </div>
    
    <!-- 用户详情 -->
    <div class="user-detail" v-if="selectedUser">
      <h3>用户详情</h3>
      <p><strong>姓名:</strong> {{ selectedUser.name }}</p>
      <p><strong>邮箱:</strong> {{ selectedUser.email }}</p>
      <p><strong>年龄:</strong> {{ selectedUser.age }}</p>
      <p><strong>城市:</strong> {{ selectedUser.address.city }}</p>
      
      <button @click="editUser(selectedUser)">编辑</button>
      <button @click="deleteUser(selectedUser.id)">删除</button>
    </div>
    
    <!-- 用户表单 -->
    <div class="user-form">
      <h3>{{ isEditing ? '编辑用户' : '添加新用户' }}</h3>
      <form @submit.prevent="handleSubmit">
        <input 
          v-model="formState.name" 
          placeholder="姓名" 
          required
        />
        <input 
          v-model="formState.email" 
          type="email" 
          placeholder="邮箱" 
          required
        />
        <input 
          v-model.number="formState.age" 
          type="number" 
          placeholder="年龄" 
          required
        />
        <input 
          v-model="formState.city" 
          placeholder="城市" 
          required
        />
        <button type="submit">{{ isEditing ? '更新' : '添加' }}</button>
        <button type="button" @click="cancelEdit">取消</button>
      </form>
    </div>
    
    <!-- 搜索和过滤 -->
    <div class="controls">
      <input 
        v-model="searchTerm" 
        placeholder="搜索用户..." 
        class="search-input"
      />
      <select v-model="filterStatus">
        <option value="all">全部</option>
        <option value="active">活跃</option>
        <option value="inactive">非活跃</option>
      </select>
    </div>
  </div>
</template>

<script>
import { ref, reactive, computed, watch } from 'vue'

export default {
  name: 'UserManager',
  setup() {
    // 响应式数据
    const users = ref([
      { id: 1, name: '张三', email: 'zhangsan@example.com', age: 25, address: { city: '北京' }, active: true },
      { id: 2, name: '李四', email: 'lisi@example.com', age: 30, address: { city: '上海' }, active: true },
      { id: 3, name: '王五', email: 'wangwu@example.com', age: 28, address: { city: '广州' }, active: false }
    ])
    
    const selectedUser = ref(null)
    const isEditing = ref(false)
    
    // 表单状态
    const formState = reactive({
      name: '',
      email: '',
      age: null,
      city: ''
    })
    
    // 搜索和过滤状态
    const searchTerm = ref('')
    const filterStatus = ref('all')
    
    // 计算属性
    const filteredUsers = computed(() => {
      let result = users.value
      
      // 应用搜索过滤
      if (searchTerm.value) {
        const searchLower = searchTerm.value.toLowerCase()
        result = result.filter(user => 
          user.name.toLowerCase().includes(searchLower) ||
          user.email.toLowerCase().includes(searchLower)
        )
      }
      
      // 应用状态过滤
      if (filterStatus.value !== 'all') {
        const isActive = filterStatus.value === 'active'
        result = result.filter(user => user.active === isActive)
      }
      
      return result
    })
    
    // 侦听器
    watch(selectedUser, (newUser) => {
      if (newUser) {
        console.log('用户被选中:', newUser.name)
      }
    })
    
    // 用户操作方法
    const selectUser = (user) => {
      selectedUser.value = user
      isEditing.value = false
    }
    
    const editUser = (user) => {
      selectedUser.value = user
      isEditing.value = true
      
      // 填充表单数据
      formState.name = user.name
      formState.email = user.email
      formState.age = user.age
      formState.city = user.address.city
    }
    
    const cancelEdit = () => {
      isEditing.value = false
      selectedUser.value = null
      Object.assign(formState, { name: '', email: '', age: null, city: '' })
    }
    
    const handleSubmit = () => {
      if (isEditing.value) {
        // 更新用户
        const userIndex = users.value.findIndex(u => u.id === selectedUser.value.id)
        if (userIndex !== -1) {
          users.value[userIndex] = {
            ...users.value[userIndex],
            name: formState.name,
            email: formState.email,
            age: formState.age,
            address: { city: formState.city }
          }
        }
      } else {
        // 添加新用户
        const newUser = {
          id: Date.now(),
          name: formState.name,
          email: formState.email,
          age: formState.age,
          address: { city: formState.city },
          active: true
        }
        users.value.push(newUser)
      }
      
      // 重置表单
      cancelEdit()
    }
    
    const deleteUser = (userId) => {
      if (confirm('确定要删除这个用户吗?')) {
        users.value = users.value.filter(user => user.id !== userId)
        if (selectedUser.value && selectedUser.value.id === userId) {
          selectedUser.value = null
        }
      }
    }
    
    return {
      // 数据
      users,
      selectedUser,
      isEditing,
      formState,
      searchTerm,
      filterStatus,
      
      // 计算属性
      filteredUsers,
      
      // 方法
      selectUser,
      editUser,
      cancelEdit,
      handleSubmit,
      deleteUser
    }
  }
}
</script>

<style scoped>
.user-manager {
  max-width: 800px;
  margin: 0 auto;
  padding: 20px;
}

.user-list, .user-detail, .user-form {
  margin-bottom: 20px;
  padding: 15px;
  border: 1px solid #ddd;
  border-radius: 5px;
}

.user-list ul {
  list-style: none;
  padding: 0;
}

.user-list li {
  padding: 10px;
  margin: 5px 0;
  cursor: pointer;
  background-color: #f5f5f5;
  border-radius: 3px;
}

.user-list li:hover,
.user-list li.selected {
  background-color: #007bff;
  color: white;
}

.controls {
  display: flex;
  gap: 10px;
  align-items: center;
  margin-bottom: 20px;
}

.search-input {
  flex: 1;
  padding: 8px;
  border: 1px solid #ddd;
  border-radius: 3px;
}

form {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

input {
  padding: 8px;
  border: 1px solid #ddd;
  border-radius: 3px;
}

button {
  padding: 8px 16px;
  border: none;
  border-radius: 3px;
  cursor: pointer;
}

button[type="submit"] {
  background-color: #28a745;
  color: white;
}

button[type="button"] {
  background-color: #6c757d;
  color: white;
}
</style>

复杂状态管理方案

使用组合函数进行逻辑复用

在大型应用中,我们经常需要将复杂的业务逻辑抽象成可复用的组合函数。

// 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
}

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

export function useApi() {
  const loading = ref(false)
  const error = ref(null)
  const data = ref(null)
  
  const request = async (apiCall) => {
    try {
      loading.value = true
      error.value = null
      const result = await apiCall()
      data.value = result
      return result
    } catch (err) {
      error.value = err.message
      throw err
    } finally {
      loading.value = false
    }
  }
  
  return {
    loading,
    error,
    data,
    request
  }
}

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

export function useUser() {
  const user = useLocalStorage('user', null)
  const isLoggedIn = computed(() => !!user.value)
  
  const login = (userData) => {
    user.value = userData
  }
  
  const logout = () => {
    user.value = null
  }
  
  return {
    user,
    isLoggedIn,
    login,
    logout
  }
}

状态管理的最佳实践

创建全局状态管理

// store/index.js
import { reactive, readonly } from 'vue'

const state = reactive({
  count: 0,
  user: null,
  theme: 'light',
  notifications: []
})

const mutations = {
  INCREMENT(state) {
    state.count++
  },
  SET_USER(state, user) {
    state.user = user
  },
  SET_THEME(state, theme) {
    state.theme = theme
  },
  ADD_NOTIFICATION(state, notification) {
    state.notifications.push(notification)
  }
}

const actions = {
  increment({ commit }) {
    commit('INCREMENT')
  },
  setUser({ commit }, user) {
    commit('SET_USER', user)
  },
  setTheme({ commit }, theme) {
    commit('SET_THEME', theme)
  },
  addNotification({ commit }, notification) {
    commit('ADD_NOTIFICATION', notification)
  }
}

export const useStore = () => {
  const store = {
    state: readonly(state),
    ...actions
  }
  
  return store
}

在组件中使用全局状态

<script>
import { computed } from 'vue'
import { useStore } from '@/store'

export default {
  setup() {
    const store = useStore()
    
    const count = computed(() => store.state.count)
    const user = computed(() => store.state.user)
    
    const increment = () => {
      store.increment()
    }
    
    const setUser = (userData) => {
      store.setUser(userData)
    }
    
    return {
      count,
      user,
      increment,
      setUser
    }
  }
}
</script>

性能优化技巧

合理使用 computed 和 watch

// 避免在计算属性中进行复杂计算
import { ref, computed } from 'vue'

export default {
  setup() {
    const items = ref([])
    
    // 好的做法:简单的计算
    const itemCount = computed(() => items.value.length)
    
    // 好的做法:缓存复杂计算结果
    const expensiveCalculation = computed(() => {
      return items.value.reduce((sum, item) => {
        // 复杂的计算逻辑
        return sum + (item.price * item.quantity)
      }, 0)
    })
    
    return {
      items,
      itemCount,
      expensiveCalculation
    }
  }
}

使用 watchEffect 优化性能

import { ref, watchEffect } from 'vue'

export default {
  setup() {
    const firstName = ref('')
    const lastName = ref('')
    const fullName = ref('')
    
    // 使用 watchEffect 自动追踪依赖
    watchEffect(() => {
      fullName.value = `${firstName.value} ${lastName.value}`
    })
    
    return {
      firstName,
      lastName,
      fullName
    }
  }
}

与 Vue 2 的对比

Options API vs Composition API

特性 Options API Composition API
代码组织 按属性类型分组 按功能逻辑分组
逻辑复用 Mixins, extends 组合函数
类型支持 较弱 更好
性能 较高 相当或更高
学习曲线 平缓 较陡峭

迁移建议

  1. 渐进式迁移:可以同时使用两种 API
  2. 优先重构复杂组件:优先考虑业务逻辑复杂的组件
  3. 保持一致性:团队内保持使用统一的开发模式
  4. 充分测试:迁移后进行全面的测试

常见问题和解决方案

1. 响应式数据丢失问题

// 错误做法
export default {
  setup() {
    const user = reactive({ name: 'John' })
    
    // 直接替换整个对象,会导致响应式丢失
    user = { name: 'Jane' } // 这样做会破坏响应式
    
    return { user }
  }
}

// 正确做法
export default {
  setup() {
    const user = reactive({ name: 'John' })
    
    // 正确地修改属性
    user.name = 'Jane'
    
    // 或者使用 Vue.set 或者直接赋值给响应式对象的属性
    // 如果需要替换整个对象,可以重新创建
    const updateUser = (newUser) => {
      Object.assign(user, newUser)
    }
    
    return { user, updateUser }
  }
}

2. 异步数据处理

import { ref, watch } from 'vue'

export default {
  setup() {
    const data = ref(null)
    const loading = ref(false)
    const error = ref(null)
    
    const fetchData = async (url) => {
      try {
        loading.value = true
        error.value = null
        
        const response = await fetch(url)
        if (!response.ok) {
          throw new Error(`HTTP error! status: ${response.status}`)
        }
        
        const result = await response.json()
        data.value = result
      } catch (err) {
        error.value = err.message
        console.error('Fetch error:', err)
      } finally {
        loading.value = false
      }
    }
    
    // 监听数据变化
    watch(data, (newData) => {
      if (newData) {
        console.log('Data updated:', newData)
      }
    })
    
    return {
      data,
      loading,
      error,
      fetchData
    }
  }
}

总结

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

  1. 基础语法:setup 函数、ref 和 reactive 的使用
  2. 核心特性:计算属性、侦听器的实现方式
  3. 实际应用:创建完整的用户管理组件
  4. 复杂状态管理:组合函数、全局状态管理的最佳实践
  5. 性能优化:合理的使用策略和性能提升技巧

Composition API 的引入让 Vue 组件更加灵活和可维护,特别是在处理复杂的业务逻辑时表现尤为突出。通过合理使用组合函数,我们可以轻松实现逻辑复用,提高开发效率。

在实际项目中,建议:

  • 从简单的组件开始尝试 Composition API
  • 逐步将复杂的逻辑抽象成可复用的组合函数
  • 注意性能优化,避免不必要的计算和监听
  • 充分利用 TypeScript 的类型支持

随着 Vue 生态系统的不断发展,Composition API 将会成为 Vue 开发的标准模式。掌握这一技术不仅能够提升开发效率,还能让我们编写出更加优雅和可维护的代码。

通过本文的实战指南,希望读者能够在实际项目中熟练运用 Composition API,构建出更加优秀的 Vue 应用程序。

相关推荐
广告位招租

相似文章

    评论 (0)

    0/2000