新项目选 Vue CLI 还是 Vite:拆成几个维度逐条对照
新项目该用 Vue CLI 还是试试 Vite,把两者放在"新旧"框架里比较,本身就问错了问题。真正该问的是几个具体维度——生态成熟度、团队对工具链的熟悉程度、有没有生产验证、插件体系是否完整。把这几条摆清楚,选择反而不难。
组里的选型会上,这个问题一度陷入"有人觉得该尝鲜、有人觉得没必要冒险"的僵局,讨论了将近一个半小时,一大半时间都花在"到底该按什么标准比"上,而不是"Vite 好不好"这件事本身。先把评判维度定下来,再逐条对照,而不是先站队再找理由,这是做技术选型时更稳妥的顺序。下面把这几个维度逐一展开,包括讨论到后半段才被补上的两条——调试体验和 CI/CD 适配成本。
维度一:生态成熟度
Vue CLI 背后是 Webpack,这条工具链走到今天已经有好几年的打磨。loader、plugin、社区问答、Stack Overflow 上的坑,基本上你能想到的场景都有人踩过、写过、修过。Vue CLI 4.x 在这套体系上又包了一层插件化的配置方式,vue.config.js 加 chainWebpack,绝大多数定制需求不需要你自己啃 Webpack 文档。
Vite 现在还在 beta 阶段,连 1.0 正式版都没到,尤雨溪今年二月才发起这个项目,满打满算七个月出头。它的开发阶段依赖原生 ES Module,思路很新,但生态几乎是从零开始积累:官方插件不多,社区插件更少,遇到问题去搜索引擎查,十有八九查不到现成答案,只能翻源码或者去 GitHub issue 里蹲。这不是说 Vite 不能用,而是你要清楚,选它就等于选了"自己趟坑"这个成本。
这一条上,Vue CLI 领先不是一星半点。
生态成熟度这条维度,本质上比的是"时间沉淀",这一点很难靠短期努力弥补。Webpack 从最初的版本到现在的 4.x,中间经历了好几年真实项目的打磨,每一个 loader、每一个 plugin 背后往往对应着社区某个具体痛点被解决的过程。这种积累不是靠某个团队突然投入更多人力就能加速追赶的,时间本身就是门槛。Vite 现在半年多的历史,无论尤雨溪个人多有影响力、无论社区反响多热烈,都没法在短时间内积累出同等量级的生态厚度,这是选型时要接受的一个客观现实,不是谁的技术能力问题。
评估的时候我特意翻了一下两边的官方仓库和第三方插件市场做对比。Vue CLI 的插件市场里,随便搜"element"能出来好几个针对 Element UI 做过按需引入优化的插件,搜"antd"能找到 Ant Design Vue 的按需加载方案,搜"i18n"能找到现成的国际化脚手架模板。这些插件不是简单能跑,而是经过大量项目验证、issue 反馈迭代过好几个版本的成熟产物。Vite 的插件列表现在只有几十个,覆盖面窄得多,很多常见需求要么没人做过,要么刚有人发了个 0.0.1 版本的实验性实现,能不能用在正式项目里完全没底。
生态成熟度不是一句空话,落到具体场景上差异很直观。比如项目里要接入图片懒加载指令、要用某个日期选择器组件、要对接内部的埋点 SDK,这些三方库在 Webpack 生态下基本都有现成的、经过验证的接入方式,npm install 完直接用,最多看一眼 README。换到 Vite,很多库要么直接不兼容——因为它还在用 CommonJS 导出、依赖 Webpack 特有的 require.context 或者 process.env 注入方式,要么需要你自己写一层适配。我在评估的时候试着往 Vite 项目里塞了一个团队内部常用的表单校验库,发现它内部用了 module.exports 混合 exports.default 的写法,Vite 的预构建(当时叫 dependency pre-bundling)处理起来会报一个模块解析的警告,最后是手动改了库的引入方式才绕过去。这种"接入一个三方库还要先看它是不是 ESM 友好"的额外工作,在 Webpack 生态里几乎不存在,因为 Webpack 对 CommonJS 的兼容处理已经打磨了很多年。
另外,生态成熟度还体现在"出问题之后能不能查到"这件事上。Webpack 相关的报错,随便复制一段堆栈信息去搜索引擎搜,大概率能找到至少一个 GitHub issue 或者博客讨论过同样的问题。Vite 现在信息量太小,很多报错搜出来是空的,只能自己对着源码走一遍打包流程,判断问题出在依赖预构建阶段还是模块请求阶段。这个排查成本,在项目工期紧张的时候会被放大。
这里还可以补一个更技术向的例子。团队项目里有个功能是根据用户角色动态加载不同的路由模块,用的是 Webpack 的动态 import() 配合路由懒加载,代码大致是这样:
1const routes = [ 2 { 3 path: '/admin', 4 component: () => import(/* webpackChunkName: "admin" */ './views/Admin.vue'), 5 }, 6 { 7 path: '/report', 8 component: () => import(/* webpackChunkName: "report" */ './views/Report.vue'), 9 }, 10];
这里的魔法注释 webpackChunkName 是 Webpack 特有的能力,用来给拆分出来的代码块命名,方便在网络面板里识别加载的是哪个模块,也方便在 CDN 缓存策略里针对不同业务模块设置不同的缓存时间。这个注释在 Vite 里没有对应写法,因为 Vite 开发环境根本不做代码分割、直接按需请求原始模块,只有生产构建阶段用 Rollup 打包时才涉及代码分割,而 Rollup 的分包命名规则和 Webpack 完全是两套逻辑,需要用 build.rollupOptions.output.manualChunks 这类配置重新实现一遍类似的效果。这种"同样的效果,两边实现方式完全不同"的情况,在评估阶段还遇到了好几处,每一处都要花时间去确认 Vite 下的对应写法是否成熟可靠。
类似的例子还有代理配置。团队本地开发环境需要把 /api 前缀的请求代理到测试服务器,Vue CLI 项目里这么写:
1// vue.config.js 2module.exports = { 3 devServer: { 4 proxy: { 5 '/api': { 6 target: 'http://test.internal.example.com', 7 changeOrigin: true, 8 pathRewrite: { '^/api': '' }, 9 }, 10 }, 11 }, 12};
Vite 里对应的写法结构上差不多,都是配置一个 proxy 对象,字段命名也高度相似,这算是两边少数几个"迁移成本几乎为零"的配置项之一。但代理这种基础能力两边一致,不代表所有 devServer 相关的配置都能直接照搬,比如 Vue CLI 里控制端口、控制是否自动打开浏览器、控制 HTTPS 证书这些配置项,字段名称和默认值都和 Vite 有细节差异,真正迁移的时候还是得逐项核对一遍配置文档,不能想当然地认为"看着差不多就能直接抄"。
生态成熟度还有一层容易被忽视的含义:中文技术社区的沉淀程度。Vue CLI 加 Webpack 这套组合,国内几个大厂的技术博客、掘金、思否上能查到大量中文实战文章,遇到"环境变量怎么区分测试和生产""多页面应用怎么配置"这类具体问题,基本能找到中文写的踩坑记录,读起来省力气。Vite 目前的中文资料几乎为零,能查到的大多是尤雨溪本人和几个海外开发者的英文讨论,团队里英文阅读没问题的同事还好,对英文技术文档不熟的同事,这条差距会被进一步放大。这不是说英文资料不能用,而是排查问题的效率会实实在在地打折扣。
维度二:团队对工具链的熟悉程度
这条经常被技术选型讨论忽略,但实际影响很大。团队里大部分人对 Webpack 的配置逻辑、常见报错、性能优化手段已经形成肌肉记忆,遇到"页面白屏""某个 loader 报错""打包体积异常"这类问题,排查路径是现成的。换到 Vite,即便文档写得再清楚,团队第一次遇到诡异问题时,排查思路是空的,只能从头摸索。
我们组现在这批人,做中后台项目三年多,对 Webpack 的调试经验是实打实攒出来的。这种经验不是文档能替代的,它体现在"看到这个报错我大概知道是哪层出的问题"这种直觉上。新工具链要重新攒这种直觉,需要时间,也需要允许犯错的空间。如果项目工期紧、团队里熟悉 Vite 的人只有一个,这条就该谨慎对待。
这一条上,团队内部资历不同的同事反应也不一样,选型会上这个分歧其实比工具本身的差异更明显。组里一位做前端六七年的资深同事,对 Webpack 配置的熟悉程度到了闭着眼都能改 chainWebpack 的地步,他对 Vite 的态度反而最保守——他关心的不是"启动快不快",而是"出了问题我能不能在半小时内定位",这条现在 Vite 给不出承诺。组里两个工作一两年的同事反应完全相反,对新工具链的接受度明显更高,一个原因是他们对 Webpack 本身也没有太深的路径依赖,学习成本上两边差距没那么大;另一个原因是 Vite 的冷启动体验确实爽——本地项目从"敲命令到页面出来"以前要等七八秒,Vite 几乎是秒开,这种直观的效率提升对刚入行不久的同事吸引力更大。另一位资深同事的态度比较中间,他更在意的是"如果我请假两周,交接给别人的时候,别人接手 Vite 项目是不是也得从头学",这其实是团队熟悉度这条维度里容易被忽略的另一面:不只是"我熟不熟",还有"团队整体熟不熟",后者决定了项目在人员流动时的风险敞口。
这种资历差异带来的态度分歧,最后没有靠"少数服从多数"解决,而是回到了"这条维度权重多大"这个问题上——如果项目是长期维护、人员会轮换的商业项目,团队整体熟悉度的权重就应该压过个别人的尝鲜意愿。
这里也想澄清一个容易被误读的点:强调团队熟悉度的权重,不代表团队应该永远回避新工具。如果每次选型都只按"团队现在熟不熟"做判断,团队的技术栈会长期停滞,几年后可能发现自己在用一套已经被淘汰的工具链,招聘和维护都会变得困难。团队熟悉度这条维度真正强调的是:在"新项目急着交付"这个具体场景下,不该让尝鲜的冲动压过交付确定性;但在"有余力做技术储备"的场景下,理应主动去啃新工具,否则团队的技术能力会原地踏步。两者不矛盾,关键是分清楚当下面对的是哪一种场景。
这个分歧当时具体是怎么表现出来的,值得多写两句。选型会开到中途,我让每个人写一句"你对 Vite 现阶段最大的顾虑或者期待是什么",收上来的答案很有意思:那位资深同事写的是"排查问题的效率会不会因为工具太新而变慢";另一位资深同事写的是"半年后如果我不在这个项目里,接手的人能不能顺利维护";两个年轻同事写的分别是"终于不用每次改完代码等半天热更新了"和"想试试这种新的构建思路,对个人技术成长有好处"。这四句话摆在一起,正好对应了"生产验证风险""团队整体熟悉度""开发体验""个人成长意愿"这几个不同的关切点,没有一个是错的,只是权重排序不一样。选型讨论最后没有强行统一到一个答案,而是承认这几种关切都合理,再回到"这是新项目还是预研"这个更高层的判断上去分流处理。
除了资历差异,团队熟悉度这条维度还有一个更具体的落地问题:新人培训成本。我们组这半年陆续接了两个实习生,带教的时候用的是现成的一套"Vue CLI 项目结构讲解"材料,从 vue.config.js 讲到常见的 chainWebpack 写法,材料是攒了一年多、改过好几版的,讲一遍新人基本能independent上手改配置。如果换成 Vite,这套培训材料要从头写,而且因为 Vite 本身还在快速变化,今天写的培训材料可能过几个月配置项就变了,材料的维护成本也要算进团队熟悉度这条维度里。这不是危言耸听——Vite 现在 1.x 内部的几个小版本之间,配置项名称和默认行为都调整过,团队内部的知识沉淀跟不上工具本身的迭代速度,等于每次升级都要重新学一遍。
团队熟悉度这条维度还牵扯到一个更细的问题:排查工具本身的能力差异。Vue CLI 项目里遇到构建异常,团队常用的排查手段是加上 --verbose 参数看详细日志,或者用 webpack-bundle-analyzer 可视化分析打包体积构成,这两个工具团队成员基本都用顺手了,遇到"打包体积突然变大"这类问题,十几分钟就能定位到是哪个依赖引入的。Vite 现在配套的分析工具还比较少,rollup-plugin-visualizer 这类工具虽然也有,但团队没人真正用它排查过实际问题,遇到体积异常这类问题,排查效率大概率不如用惯了的 Webpack 分析工具高。这种"工具本身用得顺不顺手"的差异,也是团队熟悉度里容易被忽略、但真出问题时影响很大的一部分。
还有一个细节是关于招聘和外部协作的。团队偶尔会引入外包或者兼职前端支援某个短期项目,市面上会 Webpack、熟悉 Vue CLI 项目结构的前端候选人基数很大,随便找个有三年经验的前端,基本都能直接上手改 vue.config.js。如果项目用 Vite,符合条件、又正好用过 Vite 的候选人少得多,外部协作的启动成本会明显增加,这也是团队熟悉度这条维度往外延伸的一层考量。
维度三:有没有生产验证
这一条是最实在的一条:Vite 现在有没有大规模项目在生产环境跑过。答案是几乎没有。它目前主要面向 Vue 3 的尝鲜项目和简单 demo,社区里能看到的案例大多是"我拿它跑了个博客""我用它试了个组件库文档站"这类轻量场景,中大型、多团队协作、长期维护的商业项目几乎找不到先例。
这跟工具好不好用是两回事。一个工具可能确实快、确实优雅,但如果它还没被大量生产项目验证过,你的项目就有可能成为踩坑的那一个。电商中后台这类项目,稳定性的权重通常高于开发体验,一旦构建链路出问题,影响的是发布节奏,这个代价团队要不要担,得提前想清楚,而不是上线前才发现。
Vue CLI 这边,从 Vue CLI 3 开始已经在无数商业项目里跑过,4.x 是在这个基础上的小版本迭代,稳定性基本没有悬念。这个量级的差距其实可以拿具体数字对照一下:Vue CLI 依赖的 webpack,在 npm 上的周下载量早就是千万级别,配套的 vue-loader、babel-loader 这些包的下载量也是同一个数量级,意味着任何一个常见坑,理论上已经被成千上万个项目踩过一遍,才轮到你。Vite 现在 npm 周下载量还在几万这个量级,跟 Webpack 生态完全不是一个数量级的验证规模。参照系放在这里,"生产验证"这条维度的差距就不只是感觉上的,是可以拿数字量出来的。
再举一个更具体的参照:社区里当时能查到的、相对靠谱的 Vite 实践案例,基本都集中在两类——一类是尤雨溪自己团队维护的文档站和示例项目,另一类是几个早期尝鲜的个人博客或者小型开源组件库的官网。这些项目共同的特点是访问量不大、迭代节奏可控、就算构建出问题也不影响真实业务。我们组做的电商中后台完全是另一个量级:日常同时在线编辑的用户有几十上百人,发布窗口卡得很紧,一旦构建产物出问题,影响的是当天的发布计划和好几个业务方的排期。这种业务量级和风险容忍度上的差距,是"生产验证"这条维度里最该被正视的部分。
生产验证这条维度还有一个隐藏的判断角度:工具的"边界场景"有没有被摸过。一个工具刚发布半年,覆盖的往往只是最主流的使用路径——单页应用、标准的组件树结构、常见的路由和状态管理搭配。真正决定一个工具是否成熟的,是那些非主流但真实存在的冷门场景:多语言站点的路由拆分、按业务线做代码分包、动态加载远程配置的组件、需要兼容老旧内部系统的 iframe 嵌入方案。这些场景在 Webpack 生态里基本都有人踩过、也有对应的解决方案,因为几年时间里足够多风格迥异的项目在这套工具链上运行过。Vite 现在还没有经历过这种"非主流场景大浪淘沙"的过程,我们评估的时候特意在预研分支里试了一下多语言路由拆分和按业务线动态 import 组件树的写法,虽然功能上勉强跑通,但配置方式和 Webpack 下的最佳实践差异很大,也没有权威资料能验证这是不是当前场景下最合理的做法,心里没底。
生产验证这条维度还可以从"故障恢复手册"的角度理解。团队现在维护着一份内部的故障排查手册,里面记录了这几年 Webpack 构建、部署过程中遇到过的十几种典型问题和对应的排查步骤,从"打包体积突然翻倍"到"某个 loader 版本升级后样式丢失",每一条都是真实事故沉淀下来的经验。这份手册的价值在于,一旦线上出现类似问题,值班同事不需要从头摸索,直接翻手册照着排查步骤走,能大幅缩短故障恢复时间。如果新项目用 Vite,这份手册基本用不上,团队等于是在没有应急预案的情况下裸奔,一旦出现故障,恢复时间会明显拉长,这也是"生产验证不足"这件事在实际运维层面的具体体现,不是抽象的风险描述。
这一条上我还专门去查了一下当时能找到的几个公开分享。国内几个大厂的技术团队在公开分享里提到过尝试 Vite 的经历,但无一例外都强调是在内部工具或者营销活动页这类短生命周期项目上小范围试用,没有一个案例是把 Vite 用在核心交易链路或者长期维护的中后台系统上。反过来看 Webpack 相关的技术分享,无论是大厂还是中小团队,随手就能翻出几十篇讲"如何用 Webpack 优化十万级用户量项目的构建速度""如何在大型单体应用里做代码分割"这类文章,这些文章背后对应的都是真实跑在生产环境、经受过用户量考验的项目。这种案例数量和量级上的差距,本身就是"生产验证"这条维度最直观的佐证。
还有一个角度是从"issue 响应速度"去看生产验证程度。我翻了一下 Webpack 和 Vite 各自仓库里近期的 issue 处理情况,Webpack 作为一个已经运行多年的项目,issue 分类清晰,社区维护者数量多,大部分常见问题能在几天内得到回应;Vite 现在核心维护者数量有限,issue 响应速度参差不齐,有的问题几小时内就有人回复,有的问题可能挂了一两周都没人处理。这不是说 Vite 团队不负责任,而是一个刚起步半年的项目,人力和精力必然有限,这是客观规律,不该苛责,但选型时要把这条现实考虑进去——如果项目中期遇到工具本身的 bug,等官方修复的时间可能没法预估。
生产验证不足带来的另一重代价是"回滚成本"。如果新项目用 Vue CLI,中途发现某个方向走不通,团队里几乎人人都能帮忙定位问题、快速调整;如果用 Vite 走了两三个月才发现某个核心场景撑不住,这时候回滚到 Webpack 意味着几乎要重写整套构建配置,代价比一开始就选错框架更大。这也是为什么"生产验证"这条维度在决策链条里,权重通常应该放在比较靠前的位置——它决定的不只是现在好不好用,还决定了"选错了能不能及时止损"。
维度四:插件体系的完整性
Vue CLI 的插件体系已经能覆盖大部分工程化需求:PWA、TypeScript、单元测试、E2E 测试、ESLint 集成,一条 vue add 命令基本能装好。背后依赖的是 Webpack 生态里那些打磨了很久的 loader 和 plugin,历史问题基本都有对应的解决方案。
vue add 这个命令本身的设计也值得多说一句。它不只是简单地跑一遍 npm install,而是会在安装插件之后自动执行一段迁移脚本,把这个插件需要的配置项自动写进 vue.config.js、自动生成必要的示例文件、甚至自动调整 package.json 里的 script 命令。团队新人第一次接触 Vue CLI 项目时,看到装完一个插件之后项目结构和配置文件都发生了合理的变化,会有一种"工具在替你考虑周全"的踏实感。Vite 现在没有这种自动化的插件安装流程,装完一个社区插件之后,配置项要自己去翻插件的 README 手动加到 vite.config.js 里,出错的概率更高,因为纯靠手动操作,少加一个参数、拼错一个字段名这种低级失误更容易发生。
这几个插件挨个拆开看,差距会更清楚。PWA 这块,Vue CLI 的 @vue/cli-plugin-pwa 装完之后,Service Worker 注册、manifest 生成、图标适配这些琐碎工作全部自动接管,你只需要往 vue.config.js 里塞几行配置调整缓存策略。Vite 这边完全没有对应的官方插件,如果新项目需要 PWA 能力,得自己手写 Service Worker 注册逻辑,manifest 文件也要手动维护,工作量直接从"装一个插件"变成"自己实现一遍"。
TypeScript 支持上,Vue CLI 的 @vue/cli-plugin-typescript 集成了 fork-ts-checker-webpack-plugin,可以做到一边用 Babel 转译加速编译速度,一边在独立进程里跑完整的类型检查,两不耽误,报错信息也会正常显示在终端和浏览器覆盖层里。Vite 目前对 TypeScript 的处理方式更直接一些,用的是 esbuild 做转译,但这一步只做语法转换、不做类型检查,意味着你要么额外配一个 vue-tsc 或者手动跑 tsc --noEmit 去查类型错误,要么干脆在开发阶段放弃实时类型检查,指望 IDE 里的类型提示兜底。这个取舍不是不能接受,但和 Vue CLI 里"开箱即有完整类型检查"的体验比,还是有明显落差。
单元测试这块,Vue CLI 的 @vue/cli-plugin-unit-jest 把 Jest 的配置、Vue 单文件组件的转换器、覆盖率报告全部打通,装完插件写个 *.spec.js 直接能跑。Vite 现在没有专门针对单测的官方方案,如果项目要写单元测试,得自己手动配 Jest,还要额外处理 Jest 默认走 CommonJS 而 Vite 项目习惯用 ESM 语法之间的转换问题,配置量并不小。
ESLint 集成也是类似的情况,Vue CLI 的 @vue/cli-plugin-eslint 能在保存文件时自动触发 lint、自动修复,和 Webpack 的编译流程绑定得很紧,报错会直接跟着热更新一起弹出来。Vite 这边目前没有等价的插件,ESLint 检查基本靠编辑器插件或者单独的 npm script 跑,跟构建流程是脱节的,没办法做到"改一行代码保存就立刻看到 lint 报错"这种联动。
E2E 测试插件 @vue/cli-plugin-e2e-cypress 同样是 Vue CLI 这边的独有能力,装完直接带一套示例用例和配置好的 Cypress 环境。Vite 这边如果要接 Cypress,得完全自己搭。
这几项加起来,能看出一个规律:Vue CLI 的插件体系不是简单的"功能列表比谁多",而是这些插件都基于同一套 Webpack 编译管线深度整合过,彼此之间不打架。Vite 的插件生态现在还处于"每个能力都要单独拼凑"的阶段,拼凑本身没问题,但拼凑需要的工程判断力和排错能力,对团队是额外要求。
我把这几个插件能力做成一张更直观的对照表,评估的时候贴在了会议纪要里:
1PWA 支持: Vue CLI 官方插件开箱即用 / Vite 无官方方案,需手写 2TypeScript: Vue CLI 编译+类型检查分离 / Vite 仅转译,类型检查需额外配 3单元测试: Vue CLI 一键接入 Jest / Vite 需手动适配 ESM 与 CJS 混用 4ESLint 集成: Vue CLI 与热更新联动 / Vite 与构建流程脱节 5E2E 测试: Vue CLI 内置 Cypress 脚手架 / Vite 需完全自建
这张表看着简单,但每一行背后都是实打实的人力投入差异。假设一个新项目要把这五项能力都补齐,用 Vue CLI 大概是五条 vue add 命令、加起来不超过一个小时的事;换成 Vite,PWA、单测、E2E 这三项都要从零手搭,保守估计至少要多花两三天时间,还不算后续踩坑排错的时间。
Vite 这边插件数量少,很多 Webpack 项目里理所当然的能力,在 Vite 里得自己想办法。比如 Webpack 项目里常见的动态导入某个目录下所有模块:
1const modules = require.context('./modules', false, /\.js$/);
翻了一圈 Vite 现在的文档,发现它压根没有对应的批量导入能力,这不是"写法不一样",是这个功能现在还不存在。真要用,只能老实把目录下的模块一个个手动 import 进来:
1import moduleA from './modules/a.js'; 2import moduleB from './modules/b.js'; 3 4const modules = { a: moduleA, b: moduleB };
这种"人家一行代码解决的事,换个工具就得自己手动维护一份清单"的落差,在生态还很早期的阶段会反复遇到,require.context 只是其中最直观的一个例子。类似的差异还体现在环境变量注入方式、CSS 预处理器配置、静态资源处理规则上,一个个对照下来,工作量并不小。
插件完整性这条维度还有一个容易被忽略的角度:插件之间的组合可靠性。Vue CLI 的官方插件设计之初就考虑了彼此叠加使用的场景,比如同时装上 TypeScript 插件和单元测试插件,两者之间的配置会自动做适配,Jest 会自动识别 .ts 文件、类型检查也会在跑测试之前完成,这种组合关系是 Vue CLI 团队专门设计和测试过的。Vite 现在插件数量本来就少,不同插件之间有没有互相冲突、组合起来能不能正常工作,很多时候没人专门验证过,只能自己实际装上去试。我评估时试过同时用一个处理 SVG 图标的社区插件和一个处理环境变量注入的社区插件,两者叠加之后出现了一次奇怪的构建报错,最后定位到是两个插件都在同一个构建钩子上做了修改、互相覆盖了对方的处理结果,这种插件组合层面的坑,在生态还小的阶段更容易遇到,因为没有足够多的人做过同样的组合、把坑提前趟平。
环境变量这块的差异我评估的时候专门记了一下。Vue CLI 项目里习惯用 .env、.env.production、.env.development 这套文件区分不同环境,变量必须以 VUE_APP_ 前缀开头才会被注入到客户端代码,这套约定团队已经很熟:
1// .env.production 2VUE_APP_API_BASE = https://api.example.com 3 4// 代码里直接用 5const apiBase = process.env.VUE_APP_API_BASE;
Vite 这边环境变量前缀约定改成了 VITE_,而且不是通过 process.env 读取,是通过 import.meta.env 这个新的访问方式:
1// .env.production 2VITE_API_BASE = https://api.example.com 3 4// 代码里要这样读 5const apiBase = import.meta.env.VITE_API_BASE;
两套写法乍看只是前缀和访问方式不同,但项目里凡是引用过 process.env.VUE_APP_* 的地方,包括一些工具函数、Mock 数据的判断逻辑、甚至个别第三方 SDK 初始化时读取的环境变量,都得挨个排查改一遍,这个工作量在中大型项目里不算小。CSS 预处理器这块倒还好,Vite 对 Sass、Less 的支持基本是"装了对应的预处理器包就能用",配置成本和 Vue CLI 差不多,这算是 Vite 里少数几个"迁移成本不高"的点。静态资源处理规则上,Vite 对小于某个阈值的图片会自动内联成 base64,这个阈值的默认值和 Webpack 的 url-loader 默认阈值不完全一致,项目里如果对图片内联有明确预期,这块也要重新核对一遍。
还有一点容易被忽略:Vite 的开发环境虽然基于原生 ESM,但生产环境构建实际上走的是 Rollup,不是"全程免打包"。这意味着 Vite 在生产构建阶段能做到什么程度,其实取决于 Rollup 插件生态是否覆盖了你项目的需求,而 Rollup 插件生态目前远不如 Webpack 丰富。开发体验的提升不能简单等同于生产构建也同样成熟。
维度五:调试和 Source Map 体验
这条是选型会上后来补的,起因是有人问了一句"出了线上问题,两边排查起来方便不方便"。这个问题很实际,值得单独列一条。
这条维度的起因值得多写一句:QA 提的这个问题背后,其实是她这半年处理过好几次线上报错定位困难的经历——有几次用户反馈的报错堆栈信息经过压缩混淆之后完全看不出来是哪个组件、哪一行代码,最后靠着 Source Map 才把问题定位到具体位置。这种真实经历让她对"新工具链在这方面成熟不成熟"格外敏感,这也是为什么这条维度虽然是选型会讨论到后半段才补的,但很快就被大家认可为一条必须考虑的维度。
Vue CLI 项目里 Source Map 的生成方式已经很成熟,devtool 配置项从 eval-cheap-module-source-map 到 source-map 一整套预设可以按开发环境和生产环境分别选,浏览器里断点调试基本能精确定位到源码的原始行列。团队这几年攒下来的经验也包括"生产环境要不要上传 Source Map 到 Sentry 这类平台""要不要把 Source Map 单独上传但不打进构建产物"这类细节判断,这些都是踩过坑之后才有的心得。
Vite 的开发环境调试体验其实相当不错,因为它走的是浏览器原生模块加载,断点调试时看到的代码基本就是源码本身,不需要经过打包器的转换映射,这一点比 Webpack 的开发环境更直接。但生产构建阶段走 Rollup,Source Map 这块的配置项和生态都还在补齐,网上能查到的资料很少,遇到线上报错要反查源码位置时,工具链的成熟度不如 Webpack 这边有底气。另外一个现实问题是,团队现在用的错误监控平台,对接方式基本是照着 Webpack + Source Map 上传这条路径走的,如果换成 Vite,这部分对接逻辑要重新验证一遍,能不能对齐还没人实际跑过。
开发阶段体验 Vite 占优,生产阶段的可观测性 Vue CLI 更让人放心,这条维度不能简单归为哪一边"赢",得看团队更在意哪个阶段的体验。
调试体验这条还有一个和团队协作相关的角度。团队里代码评审的时候,经常需要在评审现场直接打开对方分支跑起来看效果、断点调试确认某个边界条件的处理是否正确。Vue CLI 项目里这套流程已经很顺畅,评审人拉下分支、npm run serve、断点调试,整个过程虽然要等启动时间,但流程本身没有意外。如果评审现场用的是 Vite 项目,虽然启动速度快了很多,但万一遇到某个还没被踩过的坑——比如某个刚合并的三方库版本和 Vite 的依赖预构建缓存产生了冲突——评审现场当场卡住去排查这种生态不成熟带来的问题,会打断评审节奏,这种情况目前概率虽然不高,但确实存在,是评估调试体验时容易被忽略的一个协作场景。
具体到调试细节上,我拿一个真实的排查场景对照过两边的体验差异。团队某天遇到一个组件在特定条件下渲染异常,需要在 computed 属性的求值过程里打断点看中间状态。在 Vue CLI 项目的开发环境下,因为 Webpack 会对源码做转换和模块包装,断点调试时偶尔会遇到"点了打断点但实际执行流程和源码顺序对不上"的情况,需要在 Source Map 配置里调整 devtool 选项才能改善,这是团队这几年攒下来的经验,知道该往哪个方向调。同样的排查场景放到 Vite 项目里,因为浏览器直接加载的就是原生 ES Module,不经过打包器的中间转换,断点调试体验反而更接近"所见即所得",这一点确实是 Vite 现阶段最直观的加分项。但这个优势仅限于开发环境——一旦涉及生产环境的报错定位,Vite 这边缺乏成熟的 Source Map 上传和管理方案,这个落差就完全反过来了。
维度六:CI/CD 流程的适配成本
这条是我自己在选型会后又补充进去的,因为构建工具的选型从来不只是本地开发体验的事,还牵扯到整条发布流水线。之所以专门补这一条,是因为选型会讨论到最后,大家的注意力基本都集中在"本地开发用起来爽不爽"上,很少有人主动提发布这一侧的事情,但发布流程一旦出问题,影响面往往比本地开发体验的问题大得多——本地开发卡顿最多影响一个人的效率,发布流程卡壳可能直接影响当天所有业务方的上线计划。
现在团队的 CI 流程,从代码提交触发流水线、跑 lint、跑单测、执行 vue-cli-service build、产物上传到 CDN,再到触发部署,每一步都是针对 Vue CLI 项目调试过的,构建耗时、缓存策略、产物目录结构都已经固化在流水线脚本里,出问题时看日志基本能一眼定位是哪个环节。这套流程稳定运行了一年多,中间只在升级 Vue CLI 大版本时调整过缓存路径。
如果换成 Vite,构建产物的目录结构、文件哈希命名规则、公共资源路径这些都会不一样,流水线里凡是硬编码了构建产物路径的地方都要重新梳理一遍。更麻烦的是,现在 CI 环境用的 Node 版本、缓存 npm 依赖的方式,都是按 Webpack 项目的习惯配置的,Vite 对 Node 版本、对某些系统层面的原生模块依赖也有自己的要求,这些兼容性问题目前网上几乎查不到资料,只能实际跑一遍流水线才知道会不会踩坑。对于新项目来说,这个成本是"从零建流水线"就顺带解决了,不算额外负担;但如果是要在现有 CI 体系里插入一个 Vite 项目,跟其余全是 Vue CLI 项目的流水线共用一套基础设施,适配成本就得单独算进选型决策里。
我们现在的部署流程里还有一个环节值得单独说:灰度发布。团队现在的灰度策略是按构建产物的文件名哈希做版本比对,决定要不要给一部分用户推送新版本、留一部分用户继续用旧版本做对照。这套灰度逻辑是紧贴着 Webpack 的文件命名规则写的,如果换成 Vite,文件哈希的生成规则、产物拆分粒度都不一样,灰度脚本这部分逻辑要重新适配,还得找一个业务量不大的时间窗口先跑一次演练,确认灰度策略在新工具链下依然可靠,这个验证成本在选型阶段就该预估进去,而不是等真正上线灰度出问题才发现。
CI 环境的稳定性也是这条维度里绕不开的一部分。团队现在的 CI 服务器跑的是内网自建的 Jenkins,Node 版本固定在某个 LTS 版本,各种缓存策略都是针对 Webpack 项目常年调优过的,比如 node_modules 的缓存 key 生成规则、构建产物的清理策略,这些细节看似琐碎,但每一处都是踩过坑之后固化下来的经验。换成 Vite 项目,这些细节要重新过一遍,尤其是 Vite 对某些系统层面的依赖(比如底层用到的 esbuild 是用 Go 编写、以原生二进制形式分发的),在内网服务器这种网络环境特殊、外网访问受限的场景下,安装依赖阶段有没有隐藏的坑,也得实际跑一遍流水线才能确认,这条在选型阶段没法凭空判断,只能通过实际验证获得答案。
另外,构建耗时这个指标也值得放在 CI/CD 这条维度里一起看。虽然 Vite 在开发环境下的启动速度明显领先,但 CI 流水线里真正在意的是生产构建耗时,这一步 Vite 走的是 Rollup,构建速度跟 Webpack 4 比起来并没有量级上的优势,甚至因为 Rollup 生态里某些插件的实现还不够优化,个别场景下构建耗时可能持平甚至略慢。也就是说,"Vite 快"这个印象主要来自开发环境的冷启动体验,套用到 CI 流水线的生产构建阶段,不能想当然地认为也会同样快,这条要单独验证,不能拿开发体验的直觉去替代生产构建的实测数据。
维度七:生产构建产物的体积和加载性能
这条是我个人一直很在意的一条,选型讨论里提得不多,但对电商中后台这类项目影响很实际——毕竟中后台也有不少同事是用配置一般的笔记本、甚至通过公司 VPN 远程访问,首屏加载速度直接影响使用体验。
Vue CLI 项目现在的生产构建,经过这几年的调优,splitChunks 配置已经打磨得比较合理,公共依赖库、业务代码、异步组件三类代码块拆分清晰,配合 CDN 的强缓存策略,用户二次访问时命中缓存的比例很高,首屏加载速度经过实测控制在可接受范围内。这套调优不是一开始就有的,是团队这两年根据实际线上数据一点点调出来的经验。
Vite 生产构建走 Rollup,理论上 Rollup 的 tree-shaking 能力比 Webpack 更彻底一些,打包出来的产物体积在某些场景下可能更小,这是 Rollup 一直以来的口碑。但代码分割这块,Rollup 的默认策略和 Webpack 不完全一样,我评估时拿现有项目的组件树在 Vite 下构建了一次,产物体积确实比 Webpack 版本小了一点,但分包粒度和 Webpack 版本比对起来有些差异,个别页面因为分包策略不同,首次加载时反而多请求了一两个额外的代码块。这种细节差异,不代表 Vite 生产构建不行,而是说明"直接套用现成的分包配置"这件事在 Vite 下还没有像 Webpack 那样有大量实践参照,需要团队自己重新摸索一遍最优配置,这个调优过程本身也是要花时间的成本。
维度八:移动端和低版本浏览器的兼容适配
这条是那位资深同事后来单独补充的,他负责过公司另一个面向门店导购的移动端管理工具,对兼容性问题一直比较敏感。中后台项目虽然主要在办公室用现代浏览器访问,但公司里还有一部分同事习惯用某些企业内网自带的旧版浏览器内核访问系统,这类场景下浏览器对新特性的支持程度参差不齐。
Vue CLI 配合 Babel 的 @vue/babel-preset-app,对目标浏览器的兼容处理已经有一整套成熟方案,通过 browserslist 配置目标浏览器范围,Babel 会自动决定要不要转译某些语法、要不要注入 polyfill,这套流程运行了很久,基本不用团队操心。Vite 的开发环境依赖浏览器原生支持 ES Module,这意味着开发阶段本身就要求浏览器版本达到一定标准,团队内部开发机都是较新版本的 Chrome,这条问题不大;但生产环境如果要兼容旧版浏览器,Vite 生态里对应的兼容方案在当时还比较新,需要额外引入一个官方还处于早期阶段的兼容插件,这块的成熟度和 Babel 生态没法比。这条维度进一步印证了此前的结论:Vite 现在更适合面向现代浏览器、内部使用、访问环境可控的场景,一旦涉及要兼容老旧浏览器环境的场景,现阶段还是 Webpack 加 Babel 这套组合更稳妥。
开发体验这一项,具体量到什么程度
前面提了好几次"开发体验是 Vite 唯一的优势项",这里值得把这个优势具体量化一下,不然容易变成一句空泛的夸奖。
我们组现在维护的中后台项目,代码量不算小,src 目录下组件加起来有大几百个文件,node_modules 体积也不小。用 Vue CLI 起本地开发服务器,从敲下 npm run serve 到浏览器里能看到页面,冷启动大概要等七到十秒,具体时间跟当天机器负载和有没有命中缓存有关系。改一个组件文件保存之后,热更新生效大概是一到两秒,如果改的是一个被很多地方引用的公共组件,热更新时间会明显变长,因为 Webpack 需要重新计算依赖图。
同样规模的项目结构,我在预研分支里用 Vite 起了一遍。冷启动这一步,因为 Vite 开发环境不需要预先把整个应用打包成一个产物,而是让浏览器按需请求各个模块,冷启动时间压缩到了一两秒以内,这个差距是数量级上的,不是"稍微快一点"这种程度。热更新这块差距更明显:改一个组件文件,从保存到浏览器里看到更新,基本是瞬时的,因为 Vite 只需要让浏览器重新请求这一个模块文件,不需要像 Webpack 那样重新计算整个依赖图再打包。
这个差距在项目规模更大的场景下会进一步放大。我特意留意了一下,Vite 的官方说明里提到过它的冷启动时间几乎不随项目规模线性增长,因为它压根不需要提前打包整个依赖图;而 Webpack 的冷启动时间是随着项目文件数量、依赖复杂度线性甚至更快增长的。也就是说,项目规模越大,两边的冷启动体验差距会越明显。团队现在这个中后台项目已经算规模不小了,如果未来继续膨胀到现在的两三倍,Vue CLI 下的冷启动时间大概率会突破十几秒甚至更久,这种情况下 Vite 的优势会体现得更充分。这也是选型讨论里一个值得记下来的伏笔:如果半年后重新评估时项目规模已经显著增长,"开发体验"这条维度的权重可能需要往上调一调。
这个体验差距,对日常开发效率的影响是实打实的。按团队现在的开发节奏,一天里保存代码触发热更新的次数少说几十次,每次省下一两秒,一天积累下来也是实打实的几分钟到十几分钟,长期看不是小数目。这也是为什么组里几个同事对 Vite 的态度还是正面的,开发体验这条确实不是虚的。
但这里要泼一盆冷水:这个体验优势是有前提的,前提是项目能顺利跑起来、且没有踩到生态不全带来的坑。一旦某个环节卡住——比如某个三方库不兼容需要花一下午去调试适配,那省下来的那点热更新时间根本抵不过排查问题的时间成本。所以开发体验这条优势不是"无条件成立"的,它建立在"项目本身的技术栈和 Vite 现阶段能覆盖的场景基本吻合"这个前提上,一旦超出这个范围,体验优势就会被排错成本反噬。
我把这个道理和团队打了个比方:Vite 现在的开发体验像是一条刚修好的高速公路,路面确实平整、车速确实能开得很快,但沿途的加油站、维修点、路牌指示这些配套设施还没建全。如果你的路线正好不需要用到这些配套,一路畅通确实很爽;但凡中途车子出点小问题需要维修,你会发现附近压根没有维修点,只能自己想办法。Vue CLI 这条路修得没那么新、车速也没那么快,但沿途配套设施齐全,出问题至少知道去哪里找援手。选哪条路,取决于你对"半路抛锚"这件事的风险容忍度有多高。
八个维度放在一起看
单看某一条,两边各有说法;八条叠在一起看,结论会清楚很多。我把它列出来对照一下:
1生态成熟度: Vue CLI 明显领先 2团队熟悉度: Vue CLI 明显领先 3生产验证: Vue CLI 明显领先,Vite 几乎为零 4插件完整性: Vue CLI 明显领先 5调试与 Source Map:开发阶段 Vite 占优,生产阶段 Vue CLI 更成熟 6CI/CD 适配成本: Vue CLI 明显更低(已有成熟流水线) 7生产构建与体积: 两边接近,Vite 分包策略需重新调优 8移动端与兼容适配: Vue CLI 明显领先(Babel 生态更成熟) 9开发体验: Vite 明显领先(这是它最突出的优势项)
这张表摆出来,与其说是"该选哪个",不如说是在提醒一个更朴素的事实:Vite 现在唯一打得过 Vue CLI 的地方,是开发阶段的启动、热更新速度和源码级调试体验,这一点确实很吸引人,但它只是众多维度里的一条,不能替代其余几条的权重。
把这八条维度按权重粗略排一下序,大致是这样的顺序:生产验证和团队熟悉度这两条权重最高,因为它们决定的是"选错了能不能扛得住、能不能及时发现问题";生态成熟度和插件完整性排在第二档,决定的是日常开发中会不会频繁遇到"这个能力得自己实现"的额外工作;CI/CD 适配成本和移动端兼容适配排第三档,属于一次性的迁移成本,摊到项目整个生命周期里权重会被稀释;调试体验和生产构建体积排最后,因为这两条两边差距没有那么悬殊,更多是"各有取舍"而不是"明显谁更好"。这个权重排序不是放之四海皆准的公式,换一个项目背景,权重顺序完全可能不一样,但至少提供了一个可以复用的排序思路。
对新项目的判断,我倾向这样处理:如果项目本身是内部工具、demo、或者面向 Vue 3 做技术预研,团队又愿意接受"生态还不全、遇到问题要自己查"这种代价,可以认真试一次 Vite,收益是实打实的开发体验提升。但如果新项目是要长期维护、多团队协作、发布节奏紧的商业项目,尤其是电商中后台这类对稳定性要求高的场景,现阶段我会优先选 Vue CLI 4.x——不是因为它更"先进",而是它在生态、团队经验、生产验证、插件完整性这几条上都没有明显短板。
这里要补充一点,这几个维度不是简单打分加总就能得出结论,权重本身要看项目性质来调整。对一个纯粹内部使用、出问题影响面很小的管理后台,"生产验证"这条的权重可以调低一些,"开发体验"的权重反而可以调高,毕竟团队每天都要在这个项目里写代码,效率提升是实打实的收益。但对一个直接面向外部客户、发布节奏和业务收入挂钩的项目,"生产验证"和"CI/CD 适配成本"这两条权重必须调到最高,哪怕开发体验差一点,稳定性上的容错空间也不能省。选型会最后达成一致意见,用的就是这套"先分场景、再定权重"的思路,而不是笼统地问"Vite 好不好"。
判断维度定下来之后,怎么验证结论
光靠讨论定维度还不够,选型会最后决定花两天时间跑一个小规模的验证,而不是纯靠讨论拍板。具体做法是拉一个和现有中后台项目结构类似、但规模小得多的分支项目,分别用 Vue CLI 4.x 和 Vite 各搭一遍,验证的重点不是"哪个能跑起来",而是逐条对照前面定下的几个维度,看实际情况和预判是否一致。
这次验证具体安排是这样的:我和一个工作两年的同事各自负责搭一套,互相不看对方的进度,最后拿结果对照,避免因为熟练程度不同导致耗时对比失真。两套项目搭建过程中遇到的问题都实时记在一个共享文档里,方便复盘时对照。
第一步是搭基础脚手架。Vue CLI 这边用 vue create 起了一个标准项目,选上 TypeScript、ESLint、Unit Testing 这几个官方插件,整个过程大概十分钟,装完直接能跑,几乎没有需要手动排查的地方。Vite 这边因为当时官方脚手架 create-vite-app 还比较简陋,选项很少,大部分能力要自己后补,搭起来花了将近一个小时,中间还遇到一次 TypeScript 支持配置不对导致类型报错的问题,翻了半天 GitHub issue 才找到解法。这一步验证的其实是"生态成熟度"和"插件完整性"这两条维度,结果和前面讨论的判断基本吻合。
第二步是接入几个团队常用的三方库,包括一个日期处理库、一个表单校验库、一个内部埋点 SDK。Vue CLI 项目里这几个库全部即插即用。Vite 项目里,日期处理库没问题,表单校验库如前面提到的,遇到了 CommonJS 混合导出的兼容问题,内部埋点 SDK 因为依赖了 window 全局对象的注入时机,跟 Vite 的模块加载顺序对不上,触发了一次调用时机错误,改了 SDK 内部的初始化时机才解决。这一步验证的是插件和生态这两条,结果同样印证了此前的判断。
第三步是跑一遍简化版的 CI 流水线,包括 lint、单测、构建三个环节。Vue CLI 项目直接复用现有流水线脚本,几乎不用改动。Vite 项目单测环节卡了比较久,因为项目里几个 .spec.js 文件用了 ESM 的 import 语法,而当时随手配的 Jest 默认走 CommonJS,需要额外装 babel-jest 做转换层,配置花了小半天时间才跑通。这一步印证了"CI/CD 适配成本"这条维度的判断。
lint 这一环节倒是没遇到太多波折,ESLint 本身不关心项目用的是哪套构建工具,规则集直接照搬过去就能跑;唯一需要调整的是针对 .vue 文件的解析器配置,团队原来用的是 vue-eslint-parser 配 babel-eslint 做底层解析,这套组合跟构建工具无关,两边项目复用同一份 .eslintrc.js 完全没问题,算是这次验证里少数"换工具链也不用重新折腾"的环节。
除了前面提到的三步,验证过程里还顺带跑了一次简化版的生产构建,对比了两边的构建产物体积和构建耗时。结果和前面维度七里聊到的一致:Vite 走 Rollup 的产物体积略小,但构建耗时因为项目规模较小、体现不出明显差异,这个结论也印证了"生产构建耗时的优势主要来自项目规模,小项目上体现不明显,真正的差异要在实际大型项目里才能看清楚"这个判断,没有再做过度引申。
这个两天的验证过程,与其说是为了得出"该选哪个"的结论,不如说是为了检验"讨论时的判断是不是凭感觉拍的"。验证下来,八个维度里除了开发体验和调试体验这两条 Vite 确实占优,其余几条的判断和实际验证结果基本一致,这也让最后的选型决定更有底气,不是单纯靠嘴上争论出来的。
新项目起步和老项目迁移,判断逻辑并不一样
这里有一点值得单独说清楚:新项目选型和老项目迁移,虽然都在讨论"要不要用 Vite",但判断逻辑其实不是一回事。新项目起步时没有历史包袱,选错了工具链,最坏的结果是重新搭一次脚手架,代价相对可控;老项目迁移则要面对已经写好的 Webpack 配置、深度绑定的插件、和部署脚本耦合的构建产物,任何一个环节没盘清楚,都可能让迁移半途而废。所以新项目选型时可以适当放宽对"生态是否完整"的容忍度,老项目迁移则必须把这条放在优先级最前面。
这也是为什么同样是聊 Vite,讨论"要不要迁移一个已有项目"和讨论"新项目起步选什么",得出的结论未必一致——前者要先算清楚迁移成本,后者要先看清楚项目本身能不能承担生态不成熟的风险。老项目迁移这件事本身牵扯的细节很多,包括怎么摸清现有 Webpack 配置里哪些是历史遗留、哪些是真正必要的定制,这部分我会在另一篇里单独展开,这里只强调一个原则:迁移决策和新项目选型决策,不能用同一套判断权重去套。
选型会上有个具体例子被拿出来对照:公司另一个团队半年前接手了一个老项目,vue.config.js 里堆了将近三百行配置,chainWebpack 里手写了十几处针对老版本浏览器兼容、按需加载第三方图表库、拆分公共代码块的定制逻辑。这些配置里,有一部分是当年为了解决具体问题写的,现在业务已经变了、条件已经不成立,属于可以删掉的历史遗留;另一部分是现在依然生效、支撑着线上真实场景的必要定制。如果这个项目要迁移到 Vite,光是把这三百行配置里"哪些该留、哪些该删、哪些要在 Vite 里找等价实现"这件事梳理清楚,保守估计就要花上一两周时间,还不算迁移过程中大概率会冒出来的新坑。这种历史包袱,新项目完全不存在,这也是为什么新项目选型和老项目迁移的决策权重必须分开算——同一个"生态是否完整"的判断标准,摆在新项目里是"能不能接受",摆在老项目里是"值不值得付出这个迁移成本",问题的性质不一样。
老项目迁移还有一个新项目不会遇到的额外风险:迁移过程中的"半成品状态"。新项目从零开始,要么用 Vue CLI 一路搭下去,要么换 Vite 从头搭,中间没有"一半用旧工具、一半用新工具"这种状态。老项目迁移则不然,往往需要一段时间内新旧工具链并存,比如先把一部分独立的子模块迁移过去验证可行性,主干还留在 Webpack 上,这种过渡期里团队要同时维护两套构建认知,排查问题时先得判断"这个坑是哪条工具链带来的",认知负担比单纯选一套工具链要重得多。这也是为什么老项目迁移的决策,天然要比新项目选型谨慎得多。
文档完整度和学习曲线也得算进去
除了插件和生态,文档本身的完整度也是一条容易被低估的维度。Vue CLI 的官方文档已经打磨了好几个版本,从项目创建、配置参考、插件开发规范到常见问题排查,每一块都有详细的中英文对照说明,遇到不确定的配置项,翻文档基本能找到明确答案,很少需要靠猜。Vue CLI 文档里甚至专门有一节讲"模式与环境变量",把开发、测试、生产这几种模式之间变量注入的差异讲得很细,团队里新人照着这节文档基本就能自己搞懂环境变量这一块的坑。
Vite 现在的官方文档还比较简略,很多配置项只有一两句话的说明,缺乏详细的使用场景举例,遇到不确定的地方经常需要直接翻源码里的类型定义或者默认配置去确认行为。我评估的时候查过几次 Vite 处理 CSS 模块化的配置项,文档里只写了参数名和类型,没有展开讲不同取值在实际项目里的效果差异,最后是自己起了个最小复现项目,试了几种取值才搞清楚区别。这种"文档说明不完整、只能靠试"的成本,在项目工期紧的时候会被放大,因为你没法提前评估好一个配置项要花多久搞清楚。
学习曲线这块还有个具体的观察角度:概念模型的迁移成本。Webpack 的核心概念——entry、output、loader、plugin——团队已经很熟,这套概念模型学一次就能迁移到几乎所有基于 Webpack 的工具(Vue CLI、Nuxt、甚至一部分 Webpack 直接手写配置的项目)。Vite 引入了一套新的概念,比如"依赖预构建"(dependency pre-bundling)、"HMR 边界"这些术语,虽然理解起来不算特别难,但对已经习惯 Webpack 心智模型的团队来说,这是额外的学习投入,而且这套心智模型目前只能用在 Vite 这一个工具上,复用性不如 Webpack 相关知识广。
团队沟通成本也是一条隐藏维度
选型会上还聊到一个容易被漏掉的点:技术选型这件事,除了工具本身的差异,还有"怎么跟团队外的人解释这个决定"的成本。产品和项目经理并不关心 Vite 的模块加载机制是原生 ESM 还是打包产物,他们关心的是"这个选择会不会让排期变得不可控"。如果团队选了一个生态还在快速变化、生产验证不足的工具,一旦项目中期遇到工具链层面的问题,需要跟非技术背景的同事解释"为什么这个坑不是我们代码写错了,而是工具本身还不成熟",这个解释成本本身就是一种隐性负担。选 Vue CLI 这种被广泛验证过的工具链,好处之一就是很少需要为工具本身的不成熟去做这种解释——出了问题基本能归因到自己代码或者配置,而不是把锅甩给"工具还太新"。这不是什么高深的判断维度,但确实是团队协作里真实存在的摩擦,选型时也值得放进考量。
这条维度还有一个更具体的场景:跨团队协作时的技术栈对齐。公司现在好几个业务线的中后台项目都是 Vue CLI 搭的,团队之间偶尔会互相借调人手支援某个紧急需求,或者共享一部分基础组件库。如果我们组单独换成 Vite,这种跨团队协作的摩擦会明显增加——借调过来的同事要先熟悉一套新工具链才能开始干活,共享组件库也要考虑两套构建工具之间的兼容性。这种"团队内部一致性"带来的协作效率,平时感觉不到,但真遇到人手紧张需要互相支援的时候,差别会很明显。选型会上有位同事提了一句"我们组用得爽,隔壁团队看不懂,这个爽值不值得",这句话某种程度上概括了这条隐藏维度的核心矛盾。
顺带提一句 Vue 3
这几天社区里 Vue 3 的 rc 版本讨论得挺热闹,正式版据说这个月就要发布了。Vite 目前的定位很大程度上就是给 Vue 3 打配合的,等 Vue 3 正式发布之后,Vite 的生态大概率会跟着热闹起来。但现在这个时间点,Vue 3 本身还没定型,Vuex、Vue Router 这些配套还没跟上,选型时把这个变量也算进去:如果新项目一开始就打算用 Vue 3 做技术预研,Vite 反而是更顺手的搭配;如果新项目还是走 Vue 2.6 这条稳妥路线,Vue CLI 4.x 配 Webpack 4 依然是眼下最没有争议的组合。
值得留意的是,就算 Vue 3 这个月正式发布,也不代表 Vite 会立刻跟着成熟起来。生态的追赶需要时间,插件作者要先适配 Vue 3 的新 API,社区案例也要慢慢积累,这中间大概率还有几个月的空窗期。选型时如果把"Vue 3 马上要正式发布了"直接等同于"Vite 马上就能用在正式项目里",是把两件事的时间线错误地对齐了。更现实的判断是,Vue 3 正式发布之后,Vite 会进入一个加速成熟期,但"加速成熟"和"已经成熟"中间还有距离,这条距离目前没人能替你担保多久能走完。
具体拆开来看,Vue 3 正式发布之后要跟上的配套至少有这么几块:状态管理这边,Vuex 4 现在还没出稳定版,社区讨论里能看到不少人在琢磨要不要换一套更轻量的状态管理方案,但目前都还是讨论阶段,没有能直接拿来用的成熟方案;路由这边,Vue Router 4 同样还在适配 Vue 3 的过程中;组件库这边,Element UI、Ant Design Vue 这些团队现在依赖的组件库,官方还没有明确的 Vue 3 版本时间表,社区里零星有人在讨论要不要照着 Vue 3 的 Composition API 风格重写一套,但都还停留在讨论阶段,没有能直接拿来用的稳定版本。这几块只要有一块没跟上,团队想把 Vue 3 用在正式项目里就要么等、要么自己垫一层适配层,两种选择成本都不低。
正因为这几块配套都还没定型,我们组现阶段对 Vue 3 的态度是"认真关注、小范围试点、不碰主力项目"。具体做法是留了一个不影响业务的内部工具页面,用 Vue 3 的 rc 版本加 Vite 搭了一遍,主要是熟悉 Composition API 的写法习惯,顺便验证一下响应式系统重写之后在实际使用中的行为差异。这种试点不追求立刻产出业务价值,追求的是"等 Vue 3 正式配套成熟的时候,团队不是从零开始学"。这个思路跟前面聊的"新项目该不该现在就上 Vite"逻辑是一致的:核心项目求稳,非核心场景保留一块试验田,两者不冲突。
一个容易忽略的反例:不是所有新项目都该选 Vue CLI
前面把 Vue CLI 说得优势明显,容易让人误以为"新项目一律选 Vue CLI"是这篇的结论,这其实是过度简化了。选型会上专门讨论过一个反例:如果新项目本身规模很小、生命周期很短、又恰好是纯前端展示型页面,比如一个只对内部小范围开放、用完即弃的活动页或者数据看板,这种项目上 Vite 反而更合适。理由很简单:这类项目的"生产验证不足"和"生态不全"这两条风险,对应的后果都很轻——即便构建链路出了问题,最坏结果是花点时间重新搭一遍,不会牵扯到发布节奏、不会影响其他业务方。而这类项目最看重的往往是"能不能快速搭起来、改起来顺不顺手",这正好是 Vite 的强项。
这个反例也提醒了一件事:前面列的八条维度不是用来算出一个放之四海皆准的答案,而是用来针对具体项目情况算权重。同一套维度、同一个团队,换一个项目背景,结论完全可能反过来。选型讨论的价值不在于"记住 Vue CLI 更好"这个结论本身,而在于"遇到新的选型问题时,知道该往哪几个方向去追问"。
关于团队分歧,最后是怎么收敛的
选型会开到最后,其实并没有出现"一方说服另一方"这种局面,资深同事依然觉得现阶段风险偏高,两个年轻同事依然对 Vite 的开发体验念念不忘。真正让讨论收敛下来的,是把"选型"和"预研"拆成了两件事分别处理。选型这件事,服务于新项目本身的交付确定性,最后按前面几条维度权重加总,落地到 Vue CLI 4.x;预研这件事,服务于团队对新工具链的储备,不占用新项目的工期和风险敞口,用团队内部一个不影响业务的工具页面去跑。这样处理之后,两边的诉求都有了着落——想稳妥交付的人不用担心新项目节奏,想尝鲜的人也有了名正言顺的试验场。
这个处理方式后来发现还有一个好处:预研积累下来的经验,会反过来影响下一次选型讨论的判断依据。比如这次讨论里"Vite 对某个表单校验库不兼容"这个具体案例,就是从预研分支里跑出来的真实结论,不是凭空猜测。等半年后再讨论一次要不要上 Vite,手里已经攒了几个月的真实使用数据,判断会比这次更扎实,不用每次都从零开始靠直觉估算。
选型维度这套方法本身也值得复用
这次选型会用到的"先定维度、再逐条对照、最后拿小范围验证做检验"这套流程,其实不只适用于 Vue CLI 和 Vite 这一次选择,团队后续遇到类似的工具链决策——比如要不要引入某个新的状态管理库、要不要把 Jest 换成别的测试框架——都可以照搬这套流程。核心思路是把"选哪个"这个笼统问题,拆解成几个可以逐条验证的具体维度,每个维度都尽量找到可以量化或者至少能对照的依据,而不是停留在"感觉哪个更好"这种主观判断上。
这套方法也有它的局限:定维度这一步本身就需要经验积累,如果团队对某个领域完全陌生,可能连"该看哪几条维度"都提炼不出来,这时候更适合的做法是先做一轮更广泛的资料调研和小范围试验,再回过头总结维度。另外要提醒一句,维度定得太多也会有反效果——这次列了八条,已经算是比较多的,如果每次选型都堆到十几条维度,逐条论证的时间成本会显著增加,讨论效率反而降低。比较合理的做法是先列一个粗略的候选清单,再挑出其中权重最高、区分度最大的五到八条深入讨论,权重接近或者区分度不明显的维度可以合并或者舍弃,不必事事俱全。这次能比较顺利地定出八条维度,很大程度上是因为团队这几年在 Webpack、CI 流程、生产事故排查这些方面已经积累了足够多的经验,才能一下子想清楚"哪几条最关键"。如果换一个团队经验积累不够,这个定维度的过程本身可能就要花更长时间,这也是选型这件事没有捷径可走的地方。
选型这件事,到最后比的从来不是哪个工具本身更炫,而是谁先把评判维度摆清楚。这次选型会开了将近一个半小时,前面一大半时间都耗在"该按什么标准比"上,真正把八条维度定下来之后,逐条对照反而只用了不到二十分钟就有了倾向性结论。新项目最终选了 Vue CLI 4.x,不是因为 Vite 不好,是因为这个项目现阶段的交付确定性权重摆在那里;团队也没有因此把 Vite 一棍子打死,预研那条口子留着,用的就是团队内部一个不影响业务的工具页面。两件事分开处理,想稳妥交付的人和想尝鲜的人,谁都不用委屈自己的判断。