在开始前,先交代下背景。我在一家做企业服务的小厂当前端负责人,手底下管着3个业务系统,用的都是Vue 2 + Element UI。去年年底老板拍板说所有新项目必须用Vue 3,我嘴上说好,心里其实一万个不情愿。毕竟Vue 2的生态熟得闭着眼睛能写,升级意味着重写组件,还得换掉一堆老依赖。但没办法,开工后我先把一个内部管理后台从Vue 2.6.14升到Vue 3.4.21,顺便把构建工具从webpack换成了Vite 5.2.8。这次升级花了我大概两周空余时间,期间遇到不少问题,也重新审视了Composition API和Options API的取舍。
说实话,官方文档一直在推Composition API,网上也一堆人吹“逻辑复用”多香。但我自己用下来,发现事情没那么简单。一开始我确实被script setup的简洁吸引,用起来很爽,比如定义一个响应式变量只要let count = ref(0),再也不用写一堆data()。但很快我就踩坑了:因为业务复杂,我把一个表单的提交逻辑拆成了useSubmit、useForm、useValidation三个composable,结果每个函数都要接收很多参数,还得小心不要丢失响应性。有一次我为了图省事,在useSubmit里直接const form = ref(useForm()),结果form对象变成了深层响应式,导致提交时数据多了一层响应式代理,后端拿到Proxy对象直接报错,我花了一天才发现是这里的问题。要是用Options API,我可能根本不会写出这种结构。
后来我冷静下来做了个简单的性能对比。拿同一个表格组件(50行×8列)分别在两种写法下渲染1000次,用Chrome Performance面板测,Composition API的脚本执行平均耗时是128ms,Options API是132ms,差别4ms约3.1%,完全可以忽略。但是打包体积上,因为Composition API需要导入更多API,在按需导入下gzip后只比Options API大了约2KB(主要来自@vue/composition-api的polyfill?不,Vue3原生自带,其实是包内代码引用更复杂导致tree-shaking效果略差)。更让我困惑的是,对于我这个项目,原来Options API里组件状态很清晰,改成Composition API后代码行数反而增加了15%,因为需要额外写很多const xxx = ref和computed。对于团队里两个写惯了Vue2的老同事,他们表示完全看不懂新代码。
当然,我不能全盘否定Composition API。如果是一个没有历史包袱的新项目,或者组件逻辑特别多,用它是很好的。但如果你和我一样要维护企业级旧项目,我建议别急着全部推翻重写。我最终的做法是:保留大部分页面用Options API,只对几个逻辑复杂的组件(比如权限树联动、动态表单)用Composition API。这样既能渐进式迁移,又能避免团队认知负担过重。具体迁移步骤是:先用@vue/compat(也许不是,但可以)?哦,我用的方法是直接从Vue2迁移到Vue3然后按需改写,但发现不值得。更好的做法是先升级到Vue3的Options API,保持代码不变,再逐步替换。这个过程中要注意$listeners、.sync这些API的移除,我列了一个清单,如果你需要我可以发在留言区。
最后说点掏心窝的话。很多人纠结用哪个其实是忘了本质:工具是为人服务的,不是为技术潮流服务的。我的个人观点是,在Vue3里Options API依然有存在的价值,尤其对于中小型组件,它更直观、更符合直觉。Composition API更适合做大型复杂应用,但前提是你得有一套自己的规范和约束,否则很容易变成一堆composable的乱麻。我后来给团队定了个规矩:每个页面的composable数量不超过2个,超过2个就拆分子组件。这比官方推荐的强制规则有用多了。希望这篇文章能帮你少走点弯路,别像我一样在调试Proxy上浪费一天。下次有机会再聊聊Vue3的teleport和Suspense,那又是另一个坑。