当前位置: 首页 > news >正文

Vue3 Composition API:深度解析与最佳实践

Vue3 Composition API:深度解析与最佳实践

前言

各位前端小伙伴,不知道你们有没有遇到过这种情况:Vue2的Options API写复杂组件时,代码组织混乱,逻辑分散!

我曾经开发过一个复杂的表单组件,使用Options API时,data、methods、computed、watch散落在各处,维护起来非常痛苦。后来我切换到Composition API,代码组织变得清晰多了!

什么是Composition API?

Composition API是Vue3引入的一种新的代码组织方式,它允许我们按照功能而非选项类型来组织代码,从而更好地复用逻辑。

Composition API vs Options API

Options API的问题

// Vue2 Options API export default { data() { return { count: 0, name: 'Vue' } }, methods: { increment() { this.count++ }, decrement() { this.count-- } }, computed: { doubledCount() { return this.count * 2 } }, watch: { count(newVal) { console.log('Count changed:', newVal) } } }

Composition API的优势

// Vue3 Composition API import { ref, computed, watch } from 'vue' export default { setup() { const count = ref(0) const name = ref('Vue') const doubledCount = computed(() => count.value * 2) function increment() { count.value++ } function decrement() { count.value-- } watch(count, (newVal) => { console.log('Count changed:', newVal) }) return { count, name, doubledCount, increment, decrement } } }

Composition API核心概念

ref和reactive

import { ref, reactive } from 'vue' // ref用于基本类型 const count = ref(0) console.log(count.value) // 0 count.value++ console.log(count.value) // 1 // reactive用于对象类型 const state = reactive({ name: 'Vue', version: 3 }) console.log(state.name) // Vue state.version = 4 console.log(state.version) // 4

computed

import { ref, computed } from 'vue' const firstName = ref('John') const lastName = ref('Doe') const fullName = computed(() => { return `${firstName.value} ${lastName.value}` }) console.log(fullName.value) // John Doe firstName.value = 'Jane' console.log(fullName.value) // Jane Doe

watch和watchEffect

import { ref, watch, watchEffect } from 'vue' const count = ref(0) // watch需要明确指定依赖 watch(count, (newVal, oldVal) => { console.log(`Count changed from ${oldVal} to ${newVal}`) }) // watchEffect自动追踪依赖 watchEffect(() => { console.log(`Current count: ${count.value}`) })

setup函数

import { ref, onMounted, onUnmounted } from 'vue' export default { setup() { const width = ref(window.innerWidth) function handleResize() { width.value = window.innerWidth } onMounted(() => { window.addEventListener('resize', handleResize) }) onUnmounted(() => { window.removeEventListener('resize', handleResize) }) return { width } } }

Composition API实战

封装可复用逻辑

// useMousePosition.js import { ref, onMounted, onUnmounted } from 'vue' export function useMousePosition() { const x = ref(0) const y = ref(0) function handleMouseMove(event) { x.value = event.clientX y.value = event.clientY } onMounted(() => { window.addEventListener('mousemove', handleMouseMove) }) onUnmounted(() => { window.removeEventListener('mousemove', handleMouseMove) }) return { x, y } } // 在组件中使用 import { useMousePosition } from './useMousePosition' export default { setup() { const { x, y } = useMousePosition() return { x, y } } }

组合多个逻辑

import { useMousePosition } from './useMousePosition' import { useLocalStorage } from './useLocalStorage' export default { setup() { const { x, y } = useMousePosition() const { savedPosition, savePosition } = useLocalStorage('position') function handleSave() { savePosition({ x: x.value, y: y.value }) } return { x, y, savedPosition, handleSave } } }

Composition API高级用法

shallowRef和shallowReactive

import { shallowRef, shallowReactive } from 'vue' // shallowRef只追踪引用变化 const shallow = shallowRef({ count: 0 }) shallow.value.count++ // 不会触发更新 shallow.value = { count: 1 } // 会触发更新 // shallowReactive只追踪顶层属性变化 const state = shallowReactive({ nested: { count: 0 } }) state.nested.count++ // 不会触发更新 state.nested = { count: 1 } // 会触发更新

triggerRef

import { shallowRef, triggerRef } from 'vue' const shallow = shallowRef({ count: 0 }) function update() { shallow.value.count++ triggerRef(shallow) // 手动触发更新 }

customRef

import { customRef } from 'vue' function useDebouncedRef(value, delay = 300) { let timeout return customRef((track, trigger) => { return { get() { track() return value }, set(newValue) { clearTimeout(timeout) timeout = setTimeout(() => { value = newValue trigger() }, delay) } } }) } // 使用 const searchQuery = useDebouncedRef('')

provide和inject

// 父组件 import { provide } from 'vue' export default { setup() { const theme = ref('dark') provide('theme', theme) return { theme } } } // 子组件 import { inject } from 'vue' export default { setup() { const theme = inject('theme') return { theme } } }

Composition API最佳实践

1. 按功能组织代码

// 不好的做法 setup() { const name = ref('') const email = ref('') const password = ref('') const isValid = computed(() => { return name.value && email.value && password.value }) function submit() { // 提交逻辑 } return { name, email, password, isValid, submit } } // 好的做法 setup() { const { name, email, password, isValid } = useForm() const { submit } = useSubmit() return { name, email, password, isValid, submit } }

2. 使用ref而非reactive

// 不好的做法 const state = reactive({ count: 0, name: 'Vue' }) // 好的做法 const count = ref(0) const name = ref('Vue')

3. 避免解构响应式对象

// 不好的做法 const state = reactive({ count: 0 }) const { count } = state // count不再是响应式的 // 好的做法 const state = reactive({ count: 0 }) // 直接使用state.count

4. 使用computed缓存计算结果

// 不好的做法 function getTotal() { return items.value.reduce((sum, item) => sum + item.price, 0) } // 好的做法 const total = computed(() => { return items.value.reduce((sum, item) => sum + item.price, 0) })

Composition API常见问题

问题1:ref需要.value,太麻烦

解决方案

  • 使用reactive替代ref
  • 在模板中不需要.value
  • 使用toRefs转换
import { reactive, toRefs } from 'vue' const state = reactive({ count: 0 }) const refs = toRefs(state) // refs.count是ref

问题2:watch不触发

解决方案

  • 确保依赖是响应式的
  • 使用正确的watch语法
  • 检查是否使用了shallowRef

问题3:setup中无法访问this

解决方案

  • 在setup中不需要this
  • 使用ref和reactive代替
  • 使用provide/inject传递数据

Composition API vs Options API对比

特性Options APIComposition API
代码组织按选项类型按功能模块
逻辑复用MixinsComposables
TypeScript支持有限优秀
代码可读性
学习曲线

总结

Composition API是Vue3最重要的更新之一。通过使用Composition API,我们可以:

  1. 更好的代码组织:按功能组织代码
  2. 更好的逻辑复用:使用Composables
  3. 更好的TypeScript支持:类型推断更准确
  4. 更好的性能:编译优化

现在,开始使用Composition API构建更好的Vue应用吧!你的同事会感谢你的!

最后一句忠告:不要为了使用Composition API而使用它,选择适合你的项目的方式!

http://www.jsqmd.com/news/853915/

相关文章:

  • 非谓语动词实战指南:解锁不定式、分词与动名词的进阶用法
  • 2026 广州天河空调移机 海珠空调维修服务前五强:拆装移机、中央空调维修清洗,靠谱实惠首选 - 广州搬家老班长
  • 从账单明细看 Taotoken 按 Token 计费模式带来的成本控制优势
  • wms系统核心功能拆解:wms系统如何提升库存准确率与作业效率
  • Nginx 是独立的反向代理 / 负载均衡软件;Ingress 是 K8s 的路由规则 API,本身不处理流量,需要 Ingress Controller(最常见就是 Nginx Ingress)
  • 告别命令盲敲:在甲骨文ARM服务器上为宝塔面板做这些安全初始化
  • 三菱PLC上位机开发避坑指南:MC协议读写D寄存器时,Float和Double到底差几个点?
  • 测试工程师的幸福感:如何在测试工作中找到成就感
  • 从化做出口怎么找财税服务商?从化出口企业找财税服务商,这6个陷阱踩了就是真金白银的损失 - 欢欢在创业
  • ExternalDNS 配置实践:自动化 DNS 记录管理
  • 从零到一:基于TrueNAS SCALE构建家庭媒体与数据备份中心
  • 2026 广州天河保洁 海珠开荒保洁前五强 开荒 上门 办公室保洁 - 广州搬家老班长
  • 不止于显示图片:在ROS2 Foxy中,用OpenCV和cv_bridge玩转摄像头图像订阅与简单处理
  • 专业视角 | 宜昌高考志愿填报的「隐形陷阱」:90%家长忽略了这三点 - 新闻快传
  • 从零到一:STM32驱动TM1637四位数码管实战解析
  • 企业如何利用多模型聚合能力构建稳定的AI客服系统
  • Vue3响应式原理:深入理解Proxy和Ref
  • 告别apt!Ubuntu 20.04下从源码编译安装ROS Noetic版UUV Simulator的保姆级教程
  • 5分钟从图片到3D模型:零基础掌握ImageToSTL图片转STL技术
  • 5元级MCU Air601实战评测:硬件兼容、LuatOS开发与ESP12F迁移指南
  • 2026 中国伺服卷板机权威实力排行榜 - 安徽工业
  • 2026 中国拼板焊设备权威实力排行榜 - 安徽工业
  • Kubernetes GitOps 实践:使用 Argo CD 实现持续部署
  • 2026 中国直缝焊机权威实力排行榜 - 安徽工业
  • 2026年餐饮酒店采购供应商推荐榜单:优质酒水供应商综合测评发布 - 资讯速览
  • 4种颠覆性组合:重构Pixelle-Video的模块化潜能
  • SPICE仿真实战:从时序分析基础到建立保持时间验证
  • 一小时快速上手BLDC电机FOC控制:从零到稳定运行的实战指南
  • 【年内检索、连续4届EI检索】第五届电力工程与电气技术学术会议(ICPEET 2026)
  • L298N驱动模块进阶玩法:用Arduino实现直流电机的软启动、缓停与速度曲线控制