Vue 3 Composition API 实战指南:从 Options API 到现代开发模式

Vue 3 Composition API 实战指南:从 Options API 到现代开发模式

Vue 3 的 Composition API 是 Vue 生态系统中最重要的变革之一。它彻底改变了我们组织和复用逻辑的方式,提供了比传统的 Options API 更灵活、更强大的编程模型。本文将深入探讨 Composition API 的核心概念,并通过实际示例展示如何从 Options API 平滑过渡到现代开发模式。

为什么需要 Composition API?

在 Options API 中,我们的代码按照选项(data、methods、computed、watch 等)进行组织。这种方式在小到中型项目中表现良好,但随着项目规模的增长,会出现几个问题:

  1. 逻辑关注点分散:相关逻辑被拆分到不同的选项中
  2. 复用困难:mixins 存在命名冲突和来源不清晰的问题
  3. 类型支持有限:TypeScript 支持不够完善

Composition API 核心概念

1. setup() 函数

setup() 是 Composition API 的入口点,它在组件实例创建之前执行:

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

export default {
  setup() {
    // 响应式数据
    const count = ref(0)
    
    // 计算属性
    const doubleCount = computed(() => count.value * 2)
    
    // 方法
    const increment = () => {
      count.value++
    }
    
    // 返回模板可用的数据和方法
    return {
      count,
      doubleCount,
      increment
    }
  }
}
</script>

<template>
  <div>
    <p>Count: {{ count }}</p>
    <p>Double: {{ doubleCount }}</p>
    <button @click="increment">Increment</button>
  </div>
</template>

2. 响应式系统:ref 和 reactive

Vue 3 提供了两种创建响应式数据的方式:

import { ref, reactive } from 'vue'

// ref:用于基本类型和对象引用
const count = ref(0)
const user = ref({ name: 'John', age: 25 })

// reactive:用于对象
const state = reactive({
  count: 0,
  user: { name: 'John', age: 25 }
})

// 访问 ref 的值需要使用 .value
console.log(count.value) // 0
count.value = 1

// reactive 对象可以直接访问
console.log(state.count) // 0
state.count = 1

3. 组合函数(Composables)

这是 Composition API 最强大的特性之一,允许我们创建可复用的逻辑单元:

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

// 在组件中使用
<script>
import { useCounter } from './useCounter'

export default {
  setup() {
    const counter = useCounter(10)
    
    return {
      ...counter
    }
  }
}
</script>

实战示例:用户搜索组件

让我们创建一个实用的用户搜索组件,展示 Composition API 的强大之处:

<script>
import { ref, computed, watch } from 'vue'
import { debounce } from 'lodash-es'

// 组合函数:用户搜索逻辑
export function useUserSearch() {
  const searchQuery = ref('')
  const users = ref([])
  const isLoading = ref(false)
  const error = 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 debouncedSearch = debounce(async () => {
    if (!searchQuery.value.trim()) {
      users.value = []
      return
    }
    
    isLoading.value = true
    error.value = null
    
    try {
      const response = await fetch(
        `https://api.example.com/users?q=${encodeURIComponent(searchQuery.value)}`
      )
      users.value = await response.json()
    } catch (err) {
      error.value = '搜索失败,请重试'
      console.error('搜索错误:', err)
    } finally {
      isLoading.value = false
    }
  }, 300)
  
  // 监听搜索查询变化
  watch(searchQuery, debouncedSearch)
  
  return {
    searchQuery,
    users: filteredUsers,
    isLoading,
    error
  }
}

// 主组件
export default {
  setup() {
    const { searchQuery, users, isLoading, error } = useUserSearch()
    
    return {
      searchQuery,
      users,
      isLoading,
      error
    }
  }
}
</script>

<template>
  <div class="user-search">
    <input
      v-model="searchQuery"
      type="text"
      placeholder="搜索用户..."
      class="search-input"
    />
    
    <div v-if="isLoading" class="loading">加载中...</div>
    <div v-else-if="error" class="error">{{ error }}</div>
    
    <ul v-else class="user-list">
      <li v-for="user in users" :key="user.id" class="user-item">
        <div class="user-info">
          <strong>{{ user.name }}</strong>
          <small>{{ user.email }}</small>
        </div>
      </li>
    </ul>
  </div>
</template>

<style scoped>
.user-search {
  max-width: 400px;
  margin: 0 auto;
}

.search-input {
  width: 100%;
  padding: 10px;
  margin-bottom: 20px;
  border: 1px solid #ddd;
  border-radius: 4px;
}

.loading, .error {
  padding: 10px;
  text-align: center;
  color: #666;
}

.error {
  color: #e74c3c;
}

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

.user-item {
  padding: 10px;
  border-bottom: 1px solid #eee;
}

.user-item:hover {
  background-color: #f5f5f5;
}

.user-info {
  display: flex;
  flex-direction: column;
}
</style>

从 Options API 迁移的最佳实践

  1. 渐进式迁移:不需要一次性重写所有组件,可以逐步迁移
  2. 使用组合函数封装逻辑:将相关逻辑提取到组合函数中
  3. 利用 TypeScript:Composition API 对 TypeScript 有更好的支持
  4. 保持组件简洁:setup() 函数应该保持简洁,复杂逻辑应该提取到组合函数中

性能优化技巧

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

// 1. 使用 watchEffect 自动追踪依赖
watchEffect(() => {
  console.log('count changed:', count.value)
})

// 2. 组件卸载时清理副作用
onMounted(() => {
  const timer = setInterval(() => {
    console.log('tick')
  }, 1000)
  
  onUnmounted(() => {
    clearInterval(timer)
  })
})

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

总结

Composition API 代表了 Vue 开发的未来方向。它通过提供更灵活的代码组织方式、更好的 TypeScript 支持和更强大的逻辑复用能力,解决了 Options API 在大型项目中的局限性。虽然学习曲线相对较陡,但一旦掌握,你将能够编写出更清晰、更可维护、更高效的 Vue 代码。

建议从新项目开始尝试 Composition API,或者在现有项目中逐步迁移。Vue 官方提供了优秀的迁移指南和工具,确保迁移过程尽可能平滑。

© 版权声明
THE END
喜欢就支持一下吧
点赞9 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容