【Vue】 Vue 3 Composition API深度解析(用“乐高积木“思维重构组件逻辑)

目录

用"乐高积木"思维重构组件逻辑

一、为什么需要 Composition API?

1.1 Options API 的局限性

二、Composition API 核心思想

2.1 组合式编程哲学

2.2 核心函数一览

三、手把手构建 Composition 组件

3.1 基础计数器示例

3.2 对比 Options API

四、深度掌握响应式系统

4.1 ref vs reactive 选择指南

4.2 响应式原理揭秘

五、高级模式实战

5.1 自定义 Hook:复用登录逻辑

5.2 组合式表单验证

六、与 Options API 混合使用

6.1 渐进式迁移策略

6.2 生命周期对应关系

七、企业级最佳实践

7.1 类型安全的 TypeScript 集成

7.2 性能优化技巧

7.3 状态共享模式

八、常见问题解决方案

问题1:响应式丢失

问题2:循环引用处理

九、实战:电商商品管理系统

总结


一、为什么需要 Composition API?

1.1 Options API 的局限性

假设你开发一个包含以下功能的复杂组件:

  • 用户身份验证

  • 实时数据图表

  • 表单验证

  • WebSocket 连接

使用 Options API 时代码组织方式:

export default {
  data() { /* 混杂各种数据 */ },
  methods: { /* 交织不同的功能逻辑 */ },
  computed: { /* 分散的计算属性 */ },
  mounted() { /* 难以追踪的生命周期 */ }
}

痛点:相关逻辑分散在不同选项中,类似把乐高积木的所有零件混在一个盒子里,难以维护和复用。

二、Composition API 核心思想

2.1 组合式编程哲学

将组件逻辑视为可组合的函数单元,类似按功能分类的乐高积木套装,暂无视图

2.2 核心函数一览

函数作用类比说明
ref创建响应式基本类型数据基础积木块
reactive创建响应式对象预制结构件
computed创建计算属性齿轮传动系统
watch/watchEffect响应数据变化执行副作用传感器装置
provide/inject跨层级组件通信无线信号传输器

三、手把手构建 Composition 组件

3.1 基础计数器示例

<template>
  <div>
    <button @click="decrement">-</button>
    <span>{{ count }}</span>
    <button @click="increment">+</button>
    <p>Double: {{ doubleCount }}</p>
  </div>
</template>

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

export default {
  setup() {
    // 响应式状态
    const count = ref(0)
    
    // 计算属性
    const doubleCount = computed(() => count.value * 2)
    
    // 方法
    const increment = () => count.value++
    const decrement = () => count.value--
    
    // 暴露给模板
    return { count, doubleCount, increment, decrement }
  }
}
</script>

3.2 对比 Options API

// Options API 实现
export default {
  data() {
    return { count: 0 }
  },
  computed: {
    doubleCount() {
      return this.count * 2
    }
  },
  methods: {
    increment() { this.count++ },
    decrement() { this.count-- }
  }
}

优势对比

  • 逻辑集中:所有计数器相关代码在 setup() 中聚合

  • 类型推断:更好的 TypeScript 支持

  • 复用能力:可轻松提取为独立函数

四、深度掌握响应式系统

4.1 ref vs reactive 选择指南

import { ref, reactive } from 'vue'

// 场景1:基本类型值
const count = ref(0) // 正确
const count = reactive({ value: 0 }) // 冗余

// 场景2:对象
const user = reactive({
  name: 'Alice',
  profile: {
    age: 25,
    address: 'New York'
  }
})

// 场景3:解构对象
const { name } = toRefs(user) // 保持响应式

黄金法则

  • 基本类型用 ref

  • 对象/数组用 reactive

  • 需要解构时使用 toRefs

4.2 响应式原理揭秘

const raw = { count: 0 }
const proxy = reactive(raw)

console.log(proxy.count === raw.count) // false
console.log(reactive(proxy) === proxy) // true (相同代理)

响应式流程图

原始对象 → Proxy 代理 → 触发依赖收集 → 数据变更时通知更新

五、高级模式实战

5.1 自定义 Hook:复用登录逻辑

// hooks/useAuth.js
import { ref } from 'vue'
import axios from 'axios'

export default function useAuth() {
  const user = ref(null)
  const error = ref(null)
  
  const login = async (email, password) => {
    try {
      const response = await axios.post('/api/login', { email, password })
      user.value = response.data
    } catch (err) {
      error.value = err.message
    }
  }
  
  const logout = () => {
    user.value = null
  }
  return { user, error, login, logout }
}

// 在组件中使用
import useAuth from '@/hooks/useAuth'

export default {
  setup() {
    const { user, error, login } = useAuth()
    return { user, error, login }
  }
}

5.2 组合式表单验证

// hooks/useFormValidation.js
import { ref, computed } from 'vue'

export default function useFormValidation() {
  const email = ref('')
  const password =ref('')
  
  const emailRules = [
    v => !!v || 'Email is required',
    v => /.+@.+\..+/.test(v) || 'Email must be valid'
  ]
  
  const passwordRules = [
    v => !!v || 'Password is required',
    v => v.length >= 6 || 'Min 6 characters'
  ]
  
  const errors = computed(() => ({
    email: emailRules.map(rule => rule(email.value)).find(error => error),
    password: passwordRules.map(rule => rule(password.value)).find(error => error)
  }))
  
  const isValid = computed(() => 
    !Object.values(errors.value).some(Boolean)
  )
  
  return { email, password, errors, isValid }
}

六、与 Options API 混合使用

6.1 渐进式迁移策略

export default {
  setup() {
    // Composition API 逻辑
    const count = ref(0)
    return { count }
  },
  
  data() {
    // Options API 数据
    return { message: 'Hello' }
  },
  
  methods: {
    // 访问 Composition 状态
    showCount() {
      console.log(this.count)
    }
  }
}

6.2 生命周期对应关系

Options APIComposition API
beforeCreate不需要 (在 setup() 前执行)
created不需要 (在 setup() 前执行)
beforeMountonBeforeMount
mountedonMounted
beforeUpdateonBeforeUpdate
updatedonUpdated
beforeUnmountonBeforeUnmount
unmountedonUnmounted

七、企业级最佳实践

7.1 类型安全的 TypeScript 集成

import { defineComponent, ref } from 'vue'

interface User {
  id: number
  name: string
  email: string
}

export default defineComponent({
  setup() {
    const user = ref<User>({
      id: 1,
      name: 'Alice',
      email: 'alice@example.com'
    })
    
    return { user }
  }
})

7.2 性能优化技巧

// 1. 使用 shallowRef 避免深层响应
import { shallowRef } from 'vue'
const bigObject = shallowRef({ ... }) 

// 2. 合理使用 watchEffect 清理
const stop = watchEffect(() => { ... })
onUnmounted(stop)

// 3. 计算属性缓存优化
const sortedList = computed(() => 
  [...items.value].sort((a,b) => a.price - b.price)
)

7.3 状态共享模式

// store/useCart.js
import { reactive } from 'vue'

const state = reactive({
  items: [],
  total: 0
})

export function useCart() {
  const addItem = (item) => {
    state.items.push(item)
    state.total += item.price
  }
  
  return { ...toRefs(state), addItem }
}

// 所有组件共享同一状态

八、常见问题解决方案

问题1:响应式丢失

错误示例

const { x, y } = reactive({ x: 1, y: 2 }) // 解构后失去响应性

正确方案

const pos = reactive({ x: 1, y: 2 })
const { x, y } = toRefs(pos) // 保持响应性

问题2:循环引用处理

const obj = reactive({
  self: null
})
obj.self = obj // 创建循环引用

// 使用 WeakMap 管理引用
const proxies = new WeakMap()
function getProxy(obj) {
  if (proxies.has(obj)) return proxies.get(obj)
  const proxy = reactive(obj)
  proxies.set(obj, proxy)
  return proxy
}

九、实战:电商商品管理系统

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

// 组合式函数调用
const { searchQuery, filteredProducts } = useProductSearch()

// 独立逻辑
const cart = ref([])
const addToCart = (product) => {
  cart.value.push(product)
}

// 生命周期
onMounted(() => {
  console.log('Component mounted')
})
</script>

<template>
  <div>
    <input v-model="searchQuery" placeholder="Search...">
    
    <div class="product-list">
      <div v-for="product in filteredProducts" :key="product.id">
        <h3>{{ product.name }}</h3>
        <button @click="addToCart(product)">Add to Cart</button>
      </div>
    </div>
    
    <p>Cart Items: {{ cart.length }}</p>
  </div>
</template>

总结

Composition API 的核心优势在于逻辑关注点分离更好的代码复用。通过将组件逻辑拆分为独立的函数单元,开发者可以像搭积木一样构建复杂应用。关键要点:

  1. 响应式基础:掌握 ref 和 reactive 的适用场景

  2. 逻辑复用:通过自定义 Hook 实现业务逻辑封装

  3. 类型安全:与 TypeScript 深度集成提升代码质量

  4. 渐进迁移:支持与 Options API 混合使用

思考题
在大型项目中,如何组织数百个组合式函数避免混乱?欢迎分享你的架构设计思路!

码字不易,点点赞呗。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值