Vue 3 Composition API 完全指南:从 Options 迁移
Vue 3 把组合式 API(Composition API)推到了前台,与 Options API 平起平坐,许多团队也借这次升级重塑了组件架构。但「从 data、methods、computed 切换到 ref、reactive、composables」并不是机械替换,背后是状态封装、逻辑复用、类型推导整套思想的转变。本文从最基础的 ref 与 reactive 取舍讲起,覆盖 computed、watch、watchEffect 的差异、生命周期 hooks 的映射、composables 取代 mixins 的工程优势、TypeScript 与 script setup 的协同、Options API 仍合适的几种场景,以及中国 Vue 生态里 Element Plus、Ant Design Vue、Naive UI、TDesign Vue 的现状,给出一份既能照顾老项目、又能指导新项目的迁移路线。读完你将不再纠结「我应该用哪种 API」,而是清楚每个选择的成本与收益。
1. 为什么需要 Composition API
Vue 2 的 Options API 把组件拆成 data、methods、computed、watch、生命周期五大块,结构清晰、对初学者极其友好。但项目规模一旦超过几十个中型组件,这种「按选项分类」的结构会暴露出三个工程化痛点:第一,相关逻辑被强制分散,一个用户搜索功能的 query、results、loading 状态散落在 data 里,处理函数散落在 methods 里,副作用散落在 watch 里,阅读时需要不断上下跳转;第二,逻辑复用只能依赖 mixins,mixins 隐式注入命名空间,多个 mixins 一旦同名就静默覆盖,调试地狱;第三,TypeScript 推导对 this 上下文几乎无能为力,Vue 2 时代的 vue-class-component、vue-property-decorator 等折中方案没能成为标准。
Composition API 的解法是把组件回归到「函数」抽象。setup 是一个普通函数,状态用 ref 或 reactive 显式声明,副作用用 watch 显式注册,所有变量都是局部作用域的 JavaScript 值。逻辑可以按业务而不是按选项类型组织,复用通过 import 一个普通函数(composable)实现,TypeScript 推导自然完整。这套思想和 React Hooks 高度同源,但 Vue 的实现避开了 Hooks 调用顺序、闭包陷阱等坑。
2. ref 与 reactive 的取舍
初学者最容易困惑的就是「我应该用 ref 还是 reactive」。从底层看,ref 是一个带 .value 字段的容器,可以包装任何值;reactive 是基于 Proxy 的对象代理,只能用于对象与数组。两者都能实现深层响应性。
实战中我推荐这样的约定:基本类型(number、string、boolean)必须用 ref,因为 reactive 不能包装原始值;对象与数组也优先用 ref,统一心智、避免新人在 reactive 上误用解构(const userName = state.name 失去响应性)。reactive 适合两类场景:明确知道是大型嵌套对象、不需要整体替换(例如富文本编辑器的内部状态树);以及 Pinia store 内部,因为 store 本身就是 reactive。
更细致的取舍:表单状态用 reactive 一次声明所有字段更紧凑(const form = reactive 圆括号内 name 冒号空字符串、email 冒号空字符串);列表数据用 ref 包裹数组方便 .value 整体替换(fetchList 后 list.value = data)。混用时务必团队达成约定,否则 code review 会反复纠缠这一类风格之争。
3. computed、watch 与 watchEffect
派生状态用 computed。它纯粹、有缓存、依赖追踪自动,最大优势是「同一个值多次读取只计算一次」。原则是:只要在模板里使用、且来自其他响应式数据的派生值,都应该用 computed,不要用 watch 反向赋值。
watch 用于「响应式依赖变化时执行副作用」,签名为 watch(source, callback)。它能拿到新值与旧值,适合发请求、写日志、触发动画。可选参数 immediate、deep、flush 控制初始触发、深度监听、刷新时机。新手最常见错误是 watch 一个 reactive 对象但忘记 deep: true,导致内部字段变化收不到通知。
watchEffect 是「立即执行 + 自动收集依赖」的副作用,写法更紧凑(watchEffect 圆括号内箭头函数 fetch state.url),但调试稍难,因为不显式声明依赖。我的建议:能用 computed 就别用 watch,能用 watch 显式声明依赖就别用 watchEffect。watchEffect 适合一次性副作用、初始化逻辑等。
4. 生命周期 hooks 的映射
Composition API 把 Options API 的生命周期重命名为带 on 前缀的函数:beforeCreate 与 created 合并到 setup 本身(setup 执行时实例已创建);beforeMount 对应 onBeforeMount;mounted 对应 onMounted;beforeUpdate、updated、beforeUnmount、unmounted 同理;errorCaptured 对应 onErrorCaptured;activated、deactivated 对应 onActivated、onDeactivated(用于 keep-alive 缓存组件)。
三个常被忽视的细节:第一,所有生命周期 hooks 必须同步注册在 setup 顶层,写在 await 之后会失效(因为微任务里 currentInstance 已切换);第二,onMounted 不会在 SSR 服务端执行,依赖 DOM 的逻辑务必放在这里;第三,onUnmounted 是清理副作用(取消订阅、关闭 WebSocket)的关键钩子,配合 watchEffect 返回的 stopHandle 可以做更精细的资源管理。
5. composables:取代 mixins 的工程化方案
composable 是「以 use 开头、返回响应式状态与函数」的普通 JavaScript 函数。一个简单的 useCounter:函数内部 const count = ref(0)、const increment 等于箭头函数 count.value 自增,最后 return 大括号内 count、increment。组件里 import 后 const 解构 count、increment 等于 useCounter 圆括号即可使用。
相比 mixins,composables 的工程优势是显式:状态来源显式(看 import 路径)、命名冲突显式(解构时立刻发现)、TypeScript 推导显式(返回值类型完整)。多个 composables 可以组合,例如 useUser 内部调用 useFetch、useStorage,状态隔离干净。
VueUse 是 composable 库的最佳教学样本,提供 useStorage(封装 localStorage 响应式)、useMouse(光标位置)、useDebounce、useClipboard 等近 200 个工具。新项目几乎必装。开发个人 composable 时遵循三个约定:以 use 开头命名;返回 ref 与函数(不是 reactive 对象,避免解构丢响应性);onUnmounted 中清理资源。
6. script setup 与 TypeScript 推导
script setup 是 Vue 3.2+ 的语法糖,把 setup 函数省略,整个 script 块都视为 setup 内部,顶层声明的变量、函数、import 自动暴露给模板。defineProps、defineEmits、defineExpose、defineModel 是编译期宏,不需要 import。
对 TypeScript 用户,script setup 提供了泛型签名 defineProps 加尖括号 title 字符串、count 可选数字 圆括号,编译时推导 props 类型,无需运行时声明。defineEmits 同理。Volar 是官方语言服务,类型检查速度比 Vue 2 时代的 Vetur 提升明显,几乎追上 TSX 体验。
实践提示:开启 vue-tsc --noEmit 作为 CI 类型检查;对组件 props 与 emit 都使用对象式签名提升可读性;导出的类型用 export type 与 InstanceType 配合,便于父组件 ref 访问子组件方法。
7. 何时 Options API 仍然合适
Composition API 不是银弹。Options API 在以下场景仍是更务实的选择:团队成员主要是初学者、非前端背景、对 JavaScript 闭包不敏感时,Options API 的 data/methods/computed 分类心智成本更低;组件逻辑简单且形态稳定(按钮、表单项、纯展示卡片)时,Options API 写出的代码常常更短;维护大量 Vue 2 历史代码时,混用两套 API 反而切换成本高,可以让老组件保持 Options API;依赖只支持 Options API 的旧组件库(少见但存在)时直接用即可。
Vue 3 完整支持 Options API,并不存在「必须迁移」的硬性约束。Composition API 的真正价值在于复杂逻辑组合、大型项目可维护性、TypeScript 推导。普通组件用哪种都能写得清楚。
8. 中国 Vue 生态:Element Plus、Ant Design Vue 与新选手
中后台 UI 库是 Vue 3 落地的关键拼图。Element Plus 由饿了么前端团队维护,是国内中后台事实标准,组件丰富(80+)、表单与表格能力出色、社区主题包多、文档中文友好,几乎所有新中后台项目都从它起步。Ant Design Vue 由 Ant Design 团队官方移植,设计语言与 Ant Design React 一致,适合公司同时维护 React 与 Vue 中后台、希望视觉统一的团队。
新选手 Naive UI 由 TuSimple 团队维护,TypeScript 优先、主题系统强大、动画细节优秀,在追求设计感的 SaaS 与开发者工具里渐渐流行。TDesign Vue 是腾讯开源的设计语言体系,组件质量高、文档专业,适合企业项目。Arco Design Vue 是字节跳动的方案,组件丰富、性能优秀。
选型建议:传统中后台、最快上手 Element Plus;与 React 中后台共存 Ant Design Vue;追求设计与 TypeScript 体验 Naive UI;大厂内部统一设计语言可考虑 TDesign 或 Arco。开发流程中可以用 JSON 格式化 调试接口、用 JSON 转 TS 类型 加速 props 与 store 类型定义。
9. 迁移路线与团队协作
对老项目,渐进式迁移路线是:先升级到 Vue 3(保留 Options API,不动业务);引入 Vite + Volar 解决工具链;新组件用 script setup + Composition API;旧组件按业务节奏改写(不要大爆炸式重写);mixins 改写为 composables 时一次只迁一个,回归测试到位。Pinia 替换 Vuex 的优先级可以放在前面,因为 Pinia API 简洁、TypeScript 推导优秀,迁移成本远低于改写所有组件。
团队协作上,约定优先于偏好:风格指南里写明 ref vs reactive 默认选择、composable 命名前缀、script setup 还是 setup() 函数(统一用 script setup)、TypeScript props 写法。code review 时关注闭包陷阱(在 watch 回调里访问已变化的 ref 旧值时不要忘记 .value)、生命周期 hook 的注册位置、composable 是否清理副作用。简历项目里展示这些工程化细节,可以用 在线简历生成器 输出 PDF。
迁移不是一蹴而就,关键是让团队对每一个 API 选择都有共识。Composition API 给了你更强大的工具,但也把更多决策权放回了开发者手中。
常见问题
ref 和 reactive 到底该怎么选?
基本类型(数字、字符串、布尔)必须用 ref,否则失去响应性。对象、数组建议优先用 ref + .value 统一心智,新人不易混淆解构丢失响应性的问题。reactive 适合明确知道是对象、且不需要整体替换的场景,例如表单状态、深层配置树。团队约定一致最重要,混用最容易出错。
computed 和 watch、watchEffect 的差异是什么?
computed 是派生状态,纯粹、有缓存、用于模板渲染;watch 是「依赖变化触发副作用」,可以拿到新旧值,适合发请求、写日志;watchEffect 是「自动收集依赖、立即执行」的副作用,适合简单场景但调试稍难。原则上能用 computed 就别用 watch,能用 watch 显式声明依赖就别用 watchEffect。
composables 比 mixins 好在哪里?
mixins 把状态和方法注入组件命名空间,多个 mixins 容易冲突且来源难追溯;composables 是普通函数,显式 import、显式调用、返回值显式解构,TypeScript 推导完整。多个 composables 可以组合,状态隔离清晰。VueUse 提供的 useStorage、useMouse 等示例就是 composables 的最佳教学样本。
什么场景下 Options API 仍然更合适?
团队成员主要是初学者或非前端背景、组件逻辑简单且形态稳定、需要维护大量 Vue 2 历史代码、依赖大量只支持 Options API 的旧组件库时,Options API 仍是务实选择。Vue 3 完整支持 Options API,并不存在「必须迁移」的硬性约束。
Element Plus 和 Ant Design Vue 在中国生态如何选?
Element Plus 由饿了么前端团队维护,是国内中后台事实标准,组件丰富、表单和表格能力出色、社区主题包多。Ant Design Vue 由 Ant Design 团队官方移植,与 Ant Design React 设计语言一致,适合公司同时维护 React 与 Vue 中后台、希望视觉统一的团队。两者都已完整支持 Vue 3 与 Composition API。