Vue 3 Composition API实战:组件状态管理与性能优化完整指南

黑暗之影姬
黑暗之影姬 2026-01-29T06:04:00+08:00
0 0 1

前言

Vue 3 的发布带来了革命性的变化,其中最引人注目的就是 Composition API 的引入。作为 Vue 生态系统的重要升级,Composition API 不仅解决了 Vue 2 中 Options API 的诸多限制,还为开发者提供了更灵活、更强大的状态管理和组件构建方式。本文将深入探讨 Composition API 的各个方面,从基础概念到高级实践,帮助开发者全面掌握这一重要技术。

什么是 Composition API

核心概念

Composition API 是 Vue 3 中引入的一种新的组件状态管理方式,它允许开发者以函数的形式组织和复用逻辑代码,而不是传统的选项式 API。与 Vue 2 中的 Options API 相比,Composition API 提供了更灵活的代码组织方式,使得复杂组件的状态管理变得更加清晰和可维护。

与 Options API 的对比

在 Vue 2 中,我们通常使用 datamethodscomputedwatch 等选项来组织组件逻辑。这种方式在简单组件中表现良好,但当组件变得复杂时,相关的逻辑会被分散到不同的选项中,导致代码难以维护和复用。

// Vue 2 Options API 示例
export default {
  data() {
    return {
      count: 0,
      name: ''
    }
  },
  computed: {
    reversedName() {
      return this.name.split('').reverse().join('')
    }
  },
  methods: {
    increment() {
      this.count++
    },
    reset() {
      this.count = 0
    }
  },
  watch: {
    count(newVal, oldVal) {
      console.log(`count changed from ${oldVal} to ${newVal}`)
    }
  }
}

而 Composition API 则将相关的逻辑组织在一起,使代码更加直观:

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

export default {
  setup() {
    const count = ref(0)
    const name = ref('')
    
    const reversedName = computed(() => {
      return name.value.split('').reverse().join('')
    })
    
    const increment = () => {
      count.value++
    }
    
    const reset = () => {
      count.value = 0
    }
    
    watch(count, (newVal, oldVal) => {
      console.log(`count changed from ${oldVal} to ${newVal}`)
    })
    
    return {
      count,
      name,
      reversedName,
      increment,
      reset
    }
  }
}

响应式数据管理

ref 和 reactive 的基本使用

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

ref 的使用

ref 用于创建响应式的原始值引用。对于基本数据类型(字符串、数字、布尔值等),应该使用 ref

import { ref } from 'vue'

export default {
  setup() {
    // 创建一个响应式的数字
    const count = ref(0)
    
    // 创建一个响应式的字符串
    const message = ref('Hello Vue')
    
    // 创建一个响应式的布尔值
    const isActive = ref(true)
    
    const increment = () => {
      count.value++ // 注意:访问时需要使用 .value
    }
    
    return {
      count,
      message,
      isActive,
      increment
    }
  }
}

reactive 的使用

reactive 用于创建响应式的对象。对于复杂的数据结构(对象、数组等),应该使用 reactive

import { reactive } from 'vue'

export default {
  setup() {
    // 创建一个响应式对象
    const user = reactive({
      name: 'John',
      age: 30,
      email: 'john@example.com'
    })
    
    // 创建一个响应式数组
    const items = reactive([
      { id: 1, name: 'Item 1' },
      { id: 2, name: 'Item 2' }
    ])
    
    const updateUser = (newName) => {
      user.name = newName
    }
    
    const addItem = (item) => {
      items.push(item)
    }
    
    return {
      user,
      items,
      updateUser,
      addItem
    }
  }
}

响应式数据的访问和修改

在 Composition API 中,访问响应式数据需要使用 .value 属性,这是与 Vue 2 的一个重要区别:

import { ref, reactive } from 'vue'

export default {
  setup() {
    const count = ref(0)
    const userInfo = reactive({
      name: 'Alice',
      age: 25
    })
    
    // 访问响应式数据
    console.log(count.value) // 0
    console.log(userInfo.name) // Alice
    
    // 修改响应式数据
    const increment = () => {
      count.value++ // 正确方式
      // count++ // 错误!不会触发响应式更新
    }
    
    const updateUserInfo = () => {
      userInfo.age = 26 // 对于 reactive 对象,直接赋值即可
    }
    
    return {
      count,
      userInfo,
      increment,
      updateUserInfo
    }
  }
}

深层嵌套响应式数据

对于深层嵌套的对象,Vue 3 会自动将所有嵌套的属性转换为响应式:

import { reactive } from 'vue'

export default {
  setup() {
    const state = reactive({
      user: {
        profile: {
          name: 'Bob',
          settings: {
            theme: 'dark',
            notifications: true
          }
        }
      }
    })
    
    const updateUserTheme = () => {
      // 这种修改会触发响应式更新
      state.user.profile.settings.theme = 'light'
    }
    
    return {
      state,
      updateUserTheme
    }
  }
}

组合式函数复用

创建可复用的组合式函数

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

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

export function useCounter(initialValue = 0) {
  const count = ref(initialValue)
  
  const increment = () => {
    count.value++
  }
  
  const decrement = () => {
    count.value--
  }
  
  const reset = () => {
    count.value = initialValue
  }
  
  return {
    count,
    increment,
    decrement,
    reset
  }
}
// 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 () => {
    try {
      loading.value = true
      error.value = null
      const response = await fetch(url)
      data.value = await response.json()
    } catch (err) {
      error.value = err.message
    } finally {
      loading.value = false
    }
  }
  
  // 在组件挂载时自动获取数据
  watch(() => url, fetchData, { immediate: true })
  
  return {
    data,
    loading,
    error,
    fetchData
  }
}

使用组合式函数

// MyComponent.vue
import { defineComponent } from 'vue'
import { useCounter } from '@/composables/useCounter'
import { useFetch } from '@/composables/useFetch'

export default defineComponent({
  name: 'MyComponent',
  setup() {
    // 使用计数器组合式函数
    const { count, increment, decrement, reset } = useCounter(10)
    
    // 使用数据获取组合式函数
    const { data, loading, error, fetchData } = useFetch('/api/users')
    
    return {
      count,
      increment,
      decrement,
      reset,
      data,
      loading,
      error,
      fetchData
    }
  }
})

高级组合式函数示例

// 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/useWindowScroll.js
import { ref, onMounted, onUnmounted } from 'vue'

export function useWindowScroll() {
  const scrollY = ref(0)
  
  const handleScroll = () => {
    scrollY.value = window.scrollY
  }
  
  onMounted(() => {
    window.addEventListener('scroll', handleScroll)
    // 初始化值
    handleScroll()
  })
  
  onUnmounted(() => {
    window.removeEventListener('scroll', handleScroll)
  })
  
  return { scrollY }
}

组件状态管理

状态的组织和管理

在大型应用中,合理组织组件状态至关重要。使用 Composition API 可以更好地管理复杂的状态:

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

export function useUserStore() {
  const users = ref([])
  const loading = ref(false)
  const error = ref(null)
  
  // 计算属性:获取活跃用户数量
  const activeUsersCount = computed(() => {
    return users.value.filter(user => user.isActive).length
  })
  
  // 计算属性:按名称排序的用户列表
  const sortedUsers = computed(() => {
    return [...users.value].sort((a, b) => a.name.localeCompare(b.name))
  })
  
  // 方法:添加用户
  const addUser = (user) => {
    users.value.push(user)
  }
  
  // 方法:更新用户
  const updateUser = (id, updates) => {
    const index = users.value.findIndex(user => user.id === id)
    if (index !== -1) {
      users.value[index] = { ...users.value[index], ...updates }
    }
  }
  
  // 方法:删除用户
  const deleteUser = (id) => {
    users.value = users.value.filter(user => user.id !== id)
  }
  
  return {
    users,
    loading,
    error,
    activeUsersCount,
    sortedUsers,
    addUser,
    updateUser,
    deleteUser
  }
}

状态管理的最佳实践

// components/UserList.vue
import { defineComponent, onMounted } from 'vue'
import { useUserStore } from '@/composables/useUserStore'

export default defineComponent({
  name: 'UserList',
  setup() {
    const { users, loading, error, activeUsersCount, sortedUsers, addUser } = useUserStore()
    
    // 在组件挂载时初始化数据
    onMounted(() => {
      // 模拟加载数据
      loadData()
    })
    
    const loadData = async () => {
      try {
        loading.value = true
        // 模拟 API 调用
        await new Promise(resolve => setTimeout(resolve, 1000))
        users.value = [
          { id: 1, name: 'Alice', email: 'alice@example.com', isActive: true },
          { id: 2, name: 'Bob', email: 'bob@example.com', isActive: false },
          { id: 3, name: 'Charlie', email: 'charlie@example.com', isActive: true }
        ]
      } catch (err) {
        error.value = err.message
      } finally {
        loading.value = false
      }
    }
    
    const handleAddUser = () => {
      addUser({
        id: Date.now(),
        name: `User ${users.value.length + 1}`,
        email: `user${users.value.length + 1}@example.com`,
        isActive: true
      })
    }
    
    return {
      users,
      loading,
      error,
      activeUsersCount,
      sortedUsers,
      handleAddUser
    }
  }
})

性能优化策略

深度响应式与浅层响应式

Vue 3 提供了不同的响应式创建方式来优化性能:

import { ref, reactive, shallowRef, shallowReactive } from 'vue'

export default {
  setup() {
    // 深层响应式 - 默认行为
    const deepObject = reactive({
      nested: {
        value: 1,
        data: [1, 2, 3]
      }
    })
    
    // 浅层响应式 - 只响应顶层属性的变化
    const shallowObject = shallowReactive({
      nested: {
        value: 1,
        data: [1, 2, 3]
      }
    })
    
    // 浅层 ref - 只响应顶层值的变化
    const shallowRefValue = shallowRef({
      nested: {
        value: 1,
        data: [1, 2, 3]
      }
    })
    
    return {
      deepObject,
      shallowObject,
      shallowRefValue
    }
  }
}

计算属性优化

合理的计算属性使用可以显著提升性能:

import { ref, computed } from 'vue'

export default {
  setup() {
    const items = ref([])
    const filterText = ref('')
    
    // 高效的计算属性 - 只在依赖变化时重新计算
    const filteredItems = computed(() => {
      if (!filterText.value) return items.value
      
      return items.value.filter(item => 
        item.name.toLowerCase().includes(filterText.value.toLowerCase())
      )
    })
    
    // 复杂计算的优化版本
    const expensiveComputed = computed({
      get: () => {
        // 复杂的计算逻辑
        return items.value.reduce((acc, item) => {
          acc[item.category] = (acc[item.category] || 0) + item.value
          return acc
        }, {})
      },
      set: (newValue) => {
        // 设置值的逻辑
        items.value = newValue.items
      }
    })
    
    return {
      items,
      filterText,
      filteredItems,
      expensiveComputed
    }
  }
}

组件渲染优化

import { defineComponent, shallowRef, markRaw } from 'vue'

export default defineComponent({
  name: 'OptimizedComponent',
  setup() {
    // 使用 shallowRef 避免不必要的响应式追踪
    const shallowData = shallowRef({
      // 大量数据,不需要响应式
      largeArray: new Array(1000).fill(0).map((_, i) => ({ id: i, value: Math.random() }))
    })
    
    // 使用 markRaw 避免对象被转换为响应式
    const rawObject = markRaw({
      // 一些不需要响应式的对象
      config: {
        version: '1.0.0',
        features: ['feature1', 'feature2']
      }
    })
    
    return {
      shallowData,
      rawObject
    }
  }
})

动态导入和懒加载

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

export default defineComponent({
  name: 'LazyLoadedComponent',
  setup() {
    const component = ref(null)
    const loading = ref(false)
    
    const loadComponent = async () => {
      try {
        loading.value = true
        // 动态导入组件
        const { default: LazyComponent } = await import('./LazyComponent.vue')
        component.value = LazyComponent
      } catch (error) {
        console.error('Failed to load component:', error)
      } finally {
        loading.value = false
      }
    }
    
    onMounted(() => {
      // 延迟加载组件
      setTimeout(() => {
        loadComponent()
      }, 1000)
    })
    
    return {
      component,
      loading
    }
  }
})

高级技巧和最佳实践

自定义指令与 Composition API 结合

// directives/focus.js
import { onMounted, onUnmounted } from 'vue'

export default {
  mounted(el, binding, vnode) {
    const focus = () => {
      el.focus()
    }
    
    // 使用 Composition API 的方式
    if (binding.value === true) {
      focus()
    }
  },
  updated(el, binding) {
    if (binding.value === true) {
      el.focus()
    }
  }
}

异步组件与性能监控

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

export default defineComponent({
  name: 'PerformanceMonitor',
  setup() {
    const startTime = performance.now()
    const metrics = ref({
      renderTime: 0,
      loadTime: 0
    })
    
    const measurePerformance = () => {
      const endTime = performance.now()
      metrics.value.renderTime = endTime - startTime
      
      // 发送性能数据到监控服务
      console.log('Render time:', metrics.value.renderTime, 'ms')
    }
    
    onMounted(() => {
      // 组件挂载时测量性能
      setTimeout(measurePerformance, 0)
    })
    
    return {
      metrics
    }
  }
})

状态持久化和恢复

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

export function usePersistentState(key, defaultValue) {
  // 从 localStorage 恢复状态
  const storedValue = localStorage.getItem(key)
  const value = ref(storedValue ? JSON.parse(storedValue) : defaultValue)
  
  // 监听状态变化并保存到 localStorage
  watch(value, (newValue) => {
    localStorage.setItem(key, JSON.stringify(newValue))
  }, { deep: true })
  
  // 提供重置功能
  const reset = () => {
    value.value = defaultValue
    localStorage.removeItem(key)
  }
  
  return {
    value,
    reset
  }
}

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

项目结构设计

// src/composables/useUserManagement.js
import { ref, computed, watch } from 'vue'

export function useUserManagement() {
  // 响应式状态
  const users = ref([])
  const loading = ref(false)
  const error = ref(null)
  const searchQuery = ref('')
  const selectedUser = ref(null)
  
  // 计算属性
  const filteredUsers = computed(() => {
    if (!searchQuery.value) return users.value
    
    const query = searchQuery.value.toLowerCase()
    return users.value.filter(user => 
      user.name.toLowerCase().includes(query) ||
      user.email.toLowerCase().includes(query)
    )
  })
  
  const activeUsers = computed(() => {
    return users.value.filter(user => user.isActive)
  })
  
  // 方法
  const fetchUsers = async () => {
    try {
      loading.value = true
      error.value = null
      
      // 模拟 API 调用
      await new Promise(resolve => setTimeout(resolve, 1000))
      
      users.value = [
        { id: 1, name: 'Alice Johnson', email: 'alice@example.com', isActive: true, role: 'admin' },
        { id: 2, name: 'Bob Smith', email: 'bob@example.com', isActive: false, role: 'user' },
        { id: 3, name: 'Charlie Brown', email: 'charlie@example.com', isActive: true, role: 'user' }
      ]
    } catch (err) {
      error.value = err.message
    } finally {
      loading.value = false
    }
  }
  
  const createUser = async (userData) => {
    try {
      loading.value = true
      const newUser = {
        id: Date.now(),
        ...userData,
        isActive: true
      }
      
      users.value.push(newUser)
      return newUser
    } catch (err) {
      error.value = err.message
      throw err
    } finally {
      loading.value = false
    }
  }
  
  const updateUser = async (id, userData) => {
    try {
      loading.value = true
      const index = users.value.findIndex(user => user.id === id)
      
      if (index !== -1) {
        users.value[index] = { ...users.value[index], ...userData }
        return users.value[index]
      }
    } catch (err) {
      error.value = err.message
      throw err
    } finally {
      loading.value = false
    }
  }
  
  const deleteUser = async (id) => {
    try {
      loading.value = true
      users.value = users.value.filter(user => user.id !== id)
    } catch (err) {
      error.value = err.message
      throw err
    } finally {
      loading.value = false
    }
  }
  
  // 监听搜索查询变化
  watch(searchQuery, () => {
    console.log('Search query changed:', searchQuery.value)
  })
  
  return {
    users,
    loading,
    error,
    searchQuery,
    selectedUser,
    filteredUsers,
    activeUsers,
    fetchUsers,
    createUser,
    updateUser,
    deleteUser
  }
}

组件实现

<!-- src/components/UserManagement.vue -->
<template>
  <div class="user-management">
    <div class="header">
      <h2>用户管理</h2>
      <div class="search-bar">
        <input 
          v-model="searchQuery" 
          placeholder="搜索用户..." 
          class="search-input"
        />
        <button @click="fetchUsers" :disabled="loading">
          {{ loading ? '加载中...' : '刷新' }}
        </button>
      </div>
    </div>
    
    <div class="stats">
      <span>总用户数: {{ users.length }}</span>
      <span>活跃用户: {{ activeUsers.length }}</span>
    </div>
    
    <div v-if="error" class="error-message">
      错误: {{ error }}
    </div>
    
    <div v-if="loading" class="loading">
      加载中...
    </div>
    
    <div v-else class="users-list">
      <div 
        v-for="user in filteredUsers" 
        :key="user.id"
        class="user-card"
        @click="selectUser(user)"
      >
        <h3>{{ user.name }}</h3>
        <p>{{ user.email }}</p>
        <span class="status" :class="{ active: user.isActive }">
          {{ user.isActive ? '活跃' : '非活跃' }}
        </span>
        <span class="role">{{ user.role }}</span>
      </div>
    </div>
    
    <div v-if="selectedUser" class="user-details">
      <h3>用户详情</h3>
      <p><strong>姓名:</strong> {{ selectedUser.name }}</p>
      <p><strong>邮箱:</strong> {{ selectedUser.email }}</p>
      <p><strong>状态:</strong> {{ selectedUser.isActive ? '活跃' : '非活跃' }}</p>
      <p><strong>角色:</strong> {{ selectedUser.role }}</p>
      <button @click="selectedUser = null">关闭</button>
    </div>
  </div>
</template>

<script>
import { defineComponent, onMounted } from 'vue'
import { useUserManagement } from '@/composables/useUserManagement'

export default defineComponent({
  name: 'UserManagement',
  setup() {
    const {
      users,
      loading,
      error,
      searchQuery,
      selectedUser,
      filteredUsers,
      activeUsers,
      fetchUsers
    } = useUserManagement()
    
    const selectUser = (user) => {
      selectedUser.value = user
    }
    
    onMounted(() => {
      fetchUsers()
    })
    
    return {
      users,
      loading,
      error,
      searchQuery,
      selectedUser,
      filteredUsers,
      activeUsers,
      fetchUsers,
      selectUser
    }
  }
})
</script>

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

.header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 20px;
  flex-wrap: wrap;
  gap: 15px;
}

.search-bar {
  display: flex;
  gap: 10px;
  align-items: center;
}

.search-input {
  padding: 8px 12px;
  border: 1px solid #ddd;
  border-radius: 4px;
  min-width: 200px;
}

.stats {
  display: flex;
  gap: 20px;
  margin-bottom: 20px;
  font-weight: bold;
}

.users-list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  gap: 15px;
}

.user-card {
  border: 1px solid #ddd;
  border-radius: 8px;
  padding: 15px;
  cursor: pointer;
  transition: all 0.3s ease;
}

.user-card:hover {
  box-shadow: 0 2px 8px rgba(0,0,0,0.1);
  transform: translateY(-2px);
}

.status {
  display: inline-block;
  padding: 4px 8px;
  border-radius: 4px;
  font-size: 12px;
  margin-right: 8px;
}

.status.active {
  background-color: #d4edda;
  color: #155724;
}

.role {
  display: inline-block;
  padding: 4px 8px;
  border-radius: 4px;
  font-size: 12px;
  background-color: #f8f9fa;
  color: #6c757d;
}

.error-message {
  color: #dc3545;
  padding: 10px;
  margin-bottom: 20px;
  border-radius: 4px;
  background-color: #f8d7da;
}

.loading {
  text-align: center;
  padding: 20px;
  font-size: 18px;
}

.user-details {
  margin-top: 30px;
  padding: 20px;
  border: 1px solid #ddd;
  border-radius: 8px;
}
</style>

总结

Vue 3 的 Composition API 为前端开发带来了革命性的变化,它不仅解决了 Vue 2 中 Options API 的诸多限制,还提供了更灵活、更强大的状态管理和组件构建方式。通过本文的详细介绍,我们可以看到:

  1. 响应式数据管理refreactive 提供了灵活的数据响应式创建方式
  2. 组合式函数复用:将逻辑封装成可复用的函数,提高代码的可维护性
  3. 组件状态管理:合理组织和管理复杂的状态逻辑
  4. 性能优化策略:通过深层/浅层响应式、计算属性优化等手段提升应用性能

在实际开发中,建议:

  • 根据项目复杂度选择合适的 API 风格
  • 合理使用组合式函数来复用逻辑
  • 注意性能优化,避免不必要的响应式追踪
  • 建立良好的代码组织结构和命名
相关推荐
广告位招租

相似文章

    评论 (0)

    0/2000