1. 前端构建工具之争,为什么一夜之间成了热门话题
不知道你们最近逛社区是什么感觉,反正我打开掘金、思否、博客园,到处都在讨论 Webpack 和 Vite 的选型问题。搜索框里"webpack 配置""vite 创建 vue3 项目""webpack 打包优化配置"这些关键词的热度一直居高不下,甚至连 "vite build --mode test" 这种非常具体的命令问题都能排上热搜。“项目还在用 Webpack,要不要换成 Vite”已经成了团队内部会上绕不开的一个话题。
这个问题的本质,其实不是“谁比谁强”这么简单。Webpack 统治前端构建领域的时间太长了,从 2015 年前后开始,几乎所有的 Vue、React 项目都在用它,它成熟的插件生态和灵活的配置能力让整个行业形成了巨大惯性。而 Vite 从 Vue 3 开始被官方推荐,凭借下一代前端工具的定位,直接把开发服务器的启动速度和热更新体验提升了一个数量级,迅速抢占了大量眼球。这两年越来越多新项目直接用 Vite 初始化,Jenkins 流水线里的构建命令从 webpack 换成了 vite build,老项目的 Webpack 优化方案也变成了团队内部的技术分享主题。
作为一名老前端,我两套工具都深度用过。今天这篇文章,我想从原理到实操,从配置到踩坑,把这两个构建工具掰开揉碎了讲清楚。不管你是刚入行的新人,还是正在纠结要不要迁移的老项目负责人,这篇文章应该能帮你把思路理顺,不再被一堆术语包围。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 核心原理差异:一个“打包器”,一个“原生 ESM 服务器”,别把两者混为一谈
2.1 Webpack 的核心工作方式:先打包,再启动
很多人每天用 npm run dev 启动项目,但对 Webpack 背后干了什么其实并不清楚。Webpack 是一个典型的重型打包器,它的工作流程是:从入口文件开始,递归解析整个项目的依赖图谱,把 JS、CSS、图片、字体、模板全部都当成“模块”,然后经过 loader 的转译处理,最终以 bundle(打包产物)的形式输出。
这个过程中最关键的一点是,Webpack 在启动开发服务器之前,就已经把整个项目的模块网络梳理完毕,并完成了一次全量打包。这就好比你要去一家餐厅吃饭,这家餐厅要求你在一进门的时候就把整本菜单上的菜全部点完,后厨把所有菜一次性做好,然后才开始上菜。对于只有几十个页面、几百个模块的小项目来说,这个过程可能只要一两秒,你感觉不到什么。但当项目膨胀到几百个路由、数千个模块、几十个大型依赖之后,启动时间就会肉眼可见地飙到几十秒甚至几分钟。我维护过一个老后台项目,代码量大概五十万行,Webpack 冷启动一次要一分半钟,开发的时候每改一行代码,热更新也经常卡顿,整个团队都在骂。
Webpack 的设计逻辑适合“构建链路复杂、需要极致控制力”的场景,它提供了几乎无限的自由度,rules、plugins、optimization 这些配置项加起来上百个,你能想到的任何构建需求,它几乎都能满足。但这份自由是有代价的:配置复杂、启动慢、排错需要经验。
2.2 Vite 的核心工作方式:让浏览器成为构建系统的一部分
Vite 的设计哲学和 Webpack 完全不同。它没有传统的“先打包再启动”的过程。在开发环境下,Vite 会启动一个开发服务器,但这个服务器不会主动去解析和打包你项目的所有模块,它只做一件事:当浏览器发起请求时,按需把对应的模块转译后返回给浏览器。
因为现代浏览器原生支持 ES Module,所以 Vite 开发服务器返回的模块就是原生的 JavaScript 文件,浏览器自己会去解析 import 语句,遇到新的模块再发起新的请求,Vite 再按需处理。整个流程被拆成了“请求-响应”的模式,启动速度几乎不受项目大小影响。刚才提到的那个五十万行老项目,如果换成 Vite 冷启动,大概率两三秒就能完成。这就是“原生 ESM 开发服务器”和“打包器”最本质的差别。
打个比方,Webpack 像是把整本书先全部翻译好再交给你读,而 Vite 是给你一本原文书,你在哪一页遇到不认识的字,它才帮你查字典翻译那一页。单页翻译自然比全文翻译快得多,而且不会因为书变厚而变慢。
2.3 Vite 为什么快?除了按需编译,还有依赖预构建
Vite 快的第二个关键原因是依赖预构建。项目里那些体积庞大、长期不会变动的第三方依赖(比如 lodash、vue、react 这些 node_modules 里的包),Vite 会在服务器启动之前用 esbuild 进行一次预打包。esbuild 是用 Go 写的,打包速度比 JavaScript 写的传统打包器快几十倍,所以即使要预构建几百个依赖,也只需要几百毫秒到一两秒。
这里有个容易踩的坑点:Vite 预构建后,依赖会被转换成 ES Module 格式,并以 hash 值命名存放。如果第三方依赖更新了,或者你改了 vite.config.js 中的 optimizeDeps 配置,Vite 会在终端提示你“依赖发生了变更,请重启开发服务器”。很多新手第一次碰到这种情况会吓一跳,以为是报错,其实只需要重启一下 dev server 就好。
还有一个非常容易被忽视的点:Vite 开发环境和生产环境用的是两套不同的构建链路。开发环境靠 esbuild + 原生 ESM,生产环境则用 Rollup 来做打包。这也是为什么你在 Vite 项目里 npm run build 时看到的构建速度没有 dev 启动那么夸张的原因之一——生产环境需要做压缩、代码分割、tree-shaking 这些深度的优化工作,没法只靠 esbuild 完成。很多人以为 Vite 生产构建也会像 esbuild 一样快,严格来说其实不是。esbuild 也能做生产构建,但 Vite 为了兼容生态、保证插件机制的统一,生产构建选择了 Rollup,这才是它现在最务实的技术选型。
3. 开发体验实测:冷启动、热更新、依赖预构建,差距到底有多大
3.1 冷启动时间对比:从“去接杯水”到“秒开”
我做了一个直观的测试。同一个 Vue 3 项目,模块数量大概是 800 个,依赖 30 个左右,分别用 Webpack 和 Vite 启动开发服务器。
Webpack 5 启动时间大约用了 14 秒,这还是在开启了持久化缓存(cache: filesystem)的情况下。如果不开启缓存,冷启动大概要 25 秒以上。启动完成之后,页面加载还需要几秒,因为要执行解析和渲染。
Vite 的启动时间不到 2 秒,浏览器打开页面的瞬间,首屏模块就开始加载,后端逻辑按需返回,整个过程体验非常顺滑。对于只有三五个模块的小页面,Vite 几乎是立即触发加载;对于大型项目的复杂页面,也只是多了按需请求的次数,并不会让开发者等待太久。
开发阶段的核心价值就是工程师的专注力。Webpack 冷启动时你还能去刷个手机,启动次数多了之后,这种中断感会直接影响进入心流的速度。Vite 在这方面的体验提升是现象级的,这是我从 Webpack 切换到 Vite 之后最强烈的感受。
3.2 HMR(热更新)到底快在哪:粒度不同,速度自然不同
热更新是开发体验的另一大核心。Webpack 的 HMR 机制是通过 websocket 通知浏览器发生了变更,然后下载被修改模块对应的更新,再重新执行模块替换。如果模块之间存在深层的依赖关系,比如你修改了一个公共组件,可能会引起一大片组件被重新渲染,Webpack 需要重新打包且更新整条依赖链相关的模块。项目大了以后,经常会出现修改一个表单组件,页面白屏两三秒的尴尬情况。
Vite 的 HMR 在原理上更轻量。因为它走的是原生 ESM,每个模块的边界非常清晰,修改一个文件只需要让浏览器重新请求这个文件,以及在 import 了这个文件的上层模块链中做精确的失效处理。Vite 内部针对 Vue、React 的插件也做了非常精细的“局部更新”,比如修改 Vue 单文件组件中的 <template>,脚本逻辑不涉及的话,组件实例会被保留,组件的状态也不会丢失。
我实际感受是,Vite 的 HMR 响应速度基本在几十毫秒量级,而且实现了模块粒度的更新,几乎没有“改一行代码,全局刷新”的体验出现。当然,Vite HMR 并不是完全没有 bug,偶尔也会遇到状态连接断开、页面没有自动刷新的情况,通常手动刷新一下就好,频率不高,在可接受范围内。
这里补充一个我在 Webpack 项目里常用的优化经验:Webpack 的 HMR 如果太慢,可以先检查 devtool 的配置。开发环境用 eval-cheap-module-source-map,生产环境用 hidden-source-map 或者直接关掉 source map,能明显减少打包时间。然后配置 optimization.runtimeChunk: 'single',加上合理 splitChunks,减少热更新时无关模块被重新编译的概率。这些都属于 Webpack 打包优化配置中比较经典的技巧,但说实话,再怎么优化,也很难达到 Vite 那种原生 ESM 方案的轻盈感。
3.3 依赖预构建的常见问题:Could not resolve、Outdated Optimize Dep
依赖预构建解决了 Vite 开发模式兼容 CJS 模块的问题,却也带来了一些新坑。最常见的问题是启动时报 Could not resolve 某某依赖。这种情况通常是某个第三方依赖里的 import 写法不规范,Vite 预构建不认识它。解决办法是去 vite.config.js 的 optimizeDeps.include 里显式加入这个依赖,或者用 resolve.alias 给它指定一个绝对路径。
还有一个高频报错是 New dependencies optimized: xxx, reloading,这个其实是 Vite 在运行时发现了一个新的依赖没有预构建,于是临时把它构建好并让页面自动刷新。这个行为本身是好用的,但如果频繁出现,说明有些依赖没有被正确声明,最好手动在 optimizeDeps.include 里写死,避免开发过程被强制刷新打断。
还有一次我遇到一个非常隐蔽的问题:某个本地工具库是以源码方式引入的,在 Webpack 项目里一切正常,换到 Vite 后却发现这个库里的 ES Module 语法被原样输出到了浏览器,导致浏览器报语法错误。排查了半天才发现是 optimizeDeps.exclude 不小心把本地工具库给排除了。在 Vite 的依赖预构建体系中,本地开发的库不应该走预构建,但确保它们能被 Vite 的 transform 流程正确处理非常关键。解决方案是在 server.fs.allow 中把库的目录加进去,并确保它在 optimizeDeps.exclude 中被排除,这样 Vite 会以源码形式进行转换,就能正常解析了。
4. 生产构建能力对比:不只是快,还要看打包质量和生态
4.1 产物构建:Vite 用 Rollup,Webpack 靠自身优化
开发体验只是第一层对比,真正决定一个构建工具能否在企业级项目中长期使用的,是它的生产构建能力。
Webpack 在生产模式会做:代码压缩(terser)、tree-shaking、根据 splitChunks 配置进行代码分割、资源指纹(contenthash)、按需加载(动态 import 拆包)、CSS 提取等等。Webpack 的能力边界几乎是“只有你想不到,没有你配不出来”。但也正是因为配置项太多,很多人用了几年的 Webpack,配置还是网上照抄的模板,哪里优化了、哪里引入了性能问题,心里其实没底。
Vite 生产环境则直接内嵌了 Rollup,这套配置生成出来的产物在 tree-shaking 和代码分割方面做得非常出色。Rollup 对 ES Module 的原生支持,使得它能在纯 ESM 代码上做得更彻底。Vite 会把 build.rollupOptions 暴露给开发者,所以如果你想对产物做精细控制,可以直接改 Rollup 的配置,能力并不逊色于 Webpack。
不过需要特别说明的是,Vite 的 plugin 生态相比 Webpack 还是有一定差距。Webpack 有非常成熟和完善的 loader/plugin 体系,比如 html-webpack-plugin、mini-css-extract-plugin、copy-webpack-plugin、provide-plugin 这些都是久经考验的。Vite 的插件体系正在快速完善,但如果你有特别冷门、特别定制化的构建需求,可能会面临“找不到对应插件”的尴尬。最稳妥的办法是自己写一个 Vite 插件,难度其实不大,Vite 也提供了非常清晰的插件 API 文档。
4.2 Webpack 打包优化配置实战:老项目提速三板斧
如果你的老项目还在用 Webpack,别急着骂,先把能优化的点打透。我服务过好几个老项目,大几百个模块那种,经过这几轮优化,构建时间基本能压到原来的 30% 左右。
第一板斧是持久化缓存。Webpack 5 内置了 cache: { type: 'filesystem' },开启后 node_modules 和 build 产物会被缓存到 node_modules/.cache 目录,二次构建直接命中缓存,速度快非常多。很多老项目没开这个配置,每次构建都从头编译,白白烧机。
第二板斧是多进程/多线程处理。Babel、ESLint、TS 这些 loader 都支持开启 thread-loader,放在需要并行处理的任务前面就能启用多线程。TerserPlugin 也支持 parallel: true 参数,压缩阶段可以占满多核 CPU。这里建议根据 CPU 核心数设置合适的并行度,不要盲目开满所有线程,否则内存占用会非常夸张。
第三板斧是 splitChunks 的精细化配置。配置 optimization.splitChunks: { chunks: 'all', cacheGroups: { vendors: { test: /[\\/]node_modules[\\/]/, priority: 10 }, common: { minChunks: 2, priority: 5 } } },就可以把第三方依赖和公共模块独立拆包,利用浏览器的缓存机制让用户二次访问时不必重复下载。contenthash 也是生产环境必须用起来的,保证文件内容变化时指纹跟着变,内容没变时指纹不变,浏览器缓存才能真正生效。
4.3 Vite 的 build 配置要点:mode、环境变量和 CDN 路径
Vite 的 build 配置相对简单,但同样有几个关键点。最容易被忽略的是 base 配置,它决定了打包后资源的前缀路径。如果你要部署到某个子路径下,比如 https://example.com/admin/,就必须把 base 设为 /admin/,否则资源路径会指向根目录,上线后全部 404。
环境变量的处理是 Vite 项目的另一个重点。Vite 默认只会暴露以 VITE_ 开头的环境变量给客户端代码,这是安全考量,防止把敏感的服务端变量泄露到浏览器。在项目里读变量要通过 import.meta.env.VITE_API_URL 这样的方式。
这里就要说到 vite build --mode test 这个命令。Vite 的 --mode test 表示指定当前的构建模式为 test,Vite 会去加载 .env.test 文件,import.meta.env.MODE 就会是 'test'。这种机制很适合多环境的打包需求。比如你有一个测试环境地址 https://test-api.example.com,一个生产环境地址 https://api.example.com,那就在根目录创建 .env.test 和 .env.production,分别写入 VITE_API_URL=https://test-api.example.com 和 VITE_API_URL=https://api.example.com。构建时分别执行 vite build --mode test 和 vite build,就能打出对应后端地址的产物。这也是 vite vue env.production 这个热词背后对应的高频困惑:很多人不知道 Vite 默认的生产模式其实会自动加载 .env.production,根本不需要额外配置。
但有个注意点需要特别提一下:.env.production 是默认对应的生产模式环境,但如果你执行的是 vite build --mode test,Vite 会优先读取 .env.test,但你原本写在 .env.production 里的变量未必会被继续加载。很多从 Vue CLI 迁过来的项目,在这里会遇到环境变量“神秘丢失”的问题,其实就是对 Vite 多环境文件加载机制理解不透导致的。建议项目里统一约定好模式清单,然后在 package.json 的 scripts 里把命令写清楚,避免靠记忆去敲。
5. 配置实操:从零搭建 Vue3、React 项目的完整记录
5.1 用 Vite 创建 Vue3 项目:官方脚手架两分钟搞定
官方推荐的创建方式很简单:
bash复制npm create vue@latest
这个命令会进入交互式引导,你可以选择 TypeScript、JSX、Vue Router、Pinia、Vitest、ESLint 等配置。相比老一代的 vue-cli create,这套脚手架的模板非常干净,不会附带一堆用不到的目录结构,也不会默认给你装上一大堆依赖。执行完之后的 vscode 开发体验也非常好,官方推荐的 Volar 插件(Vue Language Features)对 Vite 项目的语法高亮和类型提示很友好,配合 Vetur 时代那种“一改文件就卡顿”的体验完全不可同日而语。
如果你更想用 Webpack 初始化 Vue3 项目,也可以继续使用 Vue CLI,执行 vue create 项目名。但要注意,Vue CLI 官方已经明确进入维护模式,不再做大的功能更新,新项目直接上 Vite 是更理性的选择。老项目如果正在用 Vue CLI,倒也不用急于迁移,毕竟 Webpack 的配置生态成熟,短期内不会有运行风险,但长期看,Vue 官方的新特性会优先支持 Vite 插件,团队的构建工具升级是迟早要面对的事。
5.2 用 Vite 创建 React 项目:和 Next.js 怎么选
Vite 官方也提供了 React 项目模板:
bash复制npm create vite@latest my-react-app -- --template react
或者如果你想用 TypeScript:
bash复制npm create vite@latest my-react-app -- --template react-ts
这套模板配置极简,开发体验和 Vue3 项目一样流畅。React 项目里比较常见的 @vitejs/plugin-react 是一个包含 JSX 转译和 Fast Refresh 的官方插件,装好之后,手动在 vite.config.ts 里配置一下:
ts复制import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [react()],
})
这里要补充一个和 Next.js 对比的问题。很多刚入行的朋友会问:“Vite + React 和 Next.js 到底有什么区别?”其实这俩根本不是同一层级的东西。Vite 是构建工具,负责把源码变成可运行的页面;Next.js 是 React 的元框架(meta framework),它本身内置了基于 Webpack(现在也支持 Turbopack)的构建链路,同时提供了服务端渲染、静态站点生成、API Routes 等能力。如果你只是做一个纯前端中后台系统、内部工具、管理系统,不需要服务端渲染,Vite + React 最合适;如果要做面向用户的官网、电商页面、需要 SEO 支持或者首屏性能要求极高,Next.js 的 SSR/SSG 能力能直接帮你搞定,这类场景用 Vite 纯客户端方案会吃亏。
我也看到很多老项目用 Next.js 不够顺手,其实是因为它自带的构建链路被封装了,想做深度的 Webpack 或 Vite 定制变得很绕。选择元框架,就意味着你要接受它给你圈定好的边界,这不是坏事,但要想清楚自己的需求再动手。
5.3 常见安装和运行报错:node_options、vite 命令找不到、vscode 相关
我几乎每天都会在社区看到下面这个问题,在 Windows 的 CMD 里执行:
bash复制$ node_options=--max-old-space-size=4096 vite
'node_options' 不是内部或外部命令
这是典型的 Windows 环境变量问题。node_options=--max-old-space-size=4096 vite 这种写法在 Linux 和 macOS 的 bash/zsh 里是能跑的,意思是临时设置 NODE_OPTIONS 环境变量为 --max-old-space-size=4096,然后再执行 vite 命令。但 Windows 的 CMD 不认这种语法,它把 node_options=--max-old-space-size=4096 当成了一条独立的命令,结果就提示“不是内部或外部命令”。
解决办法有三种。第一种,在 CMD 中要写成:
bash复制set NODE_OPTIONS=--max-old-space-size=4096 && vite
第二种,在 PowerShell 中要写成:
powershell复制$env:NODE_OPTIONS="--max-old-space-size=4096"; vite
第三种,用 cross-env 库统一不同平台的语法:
bash复制npx cross-env NODE_OPTIONS=--max-old-space-size=4096 vite
我个人比较推荐第三种,因为它能在 package.json 的 scripts 中跨平台运行,不会因为同事用 macOS 还是 Windows 而导致行为不一致。跨端开发协作时,保持构建命令的可移植性比什么都重要。
如果执行 vite 命令时提示“vite 不是内部或外部命令”,通常就是依赖没有安装完整。建议把 node_modules 删掉重装,或者检查下 npm registry 是否配置正确。npm 源的问题最常见的表现是node_modules/.bin/vite文件缺失,执行npm ls vite就能快速判断。
vscode 里使用 Vite 项目还有几个小技巧。一是设置里把 typescript.tsdk 指向项目自己的 node_modules,避免使用全局 TS 版本导致类型提示错乱;二是用 Volar 的时候要开启 Vue 的 Takeover Mode,否则可能加载两套 TS 服务导致卡顿;三是推荐安装 EditorConfig for VS Code,保证团队缩进风格一致。这些配置虽然不直接决定构建速度,但对日常开发的舒适度影响非常大。
6. 框架生态适配:Vue3、React、微前端、老项目迁移
6.1 Vue3 项目:vite 是官方推荐,webpack 是历史惯性
Vue 3 从 2021 年正式版发布开始,官方就一直推荐使用 Vite 作为构建工具。在 create-vue 的生态里,Vite 已经成了事实标准。Vue 3 的@vitejs/plugin-vue插件实现了单文件组件的编译、热更新、样式作用域等功能,使用体验远超 Vue CLI + Vue Loader 时代。
但我在服务过的多个 Vue3 企业项目中,还是看到不少团队依然在用 Webpack。原因无非是:老项目用了 Webpack,公司内部有规范模板,或者有私服需要配合。这类项目如果继续用 Webpack,其实也完全没问题,Webpack 5 的持久化缓存、多线程能力已经能把大部分体验问题解决掉。但要注意,Vue 3 的官方文档中示例都是基于 Vite 的,如果你在 Webpack 项目里遇到了 Vue 3 编译相关的问题,比如 custom renderer 或 script setup 的编译细节,可能就要花更多时间去翻源码和 issue。
6.2 微前端架构下的构建工具困境
微前端是一个非常依赖构建工具的架构。目前业界主流的 qiankun 方案,本质上还是基于 Webpack 的 Module Federation 能力来实现子应用之间的共享模块。而使用 Vite 搭建微前端时,会遇到不少兼容性问题,因为 Vite 的 dev server 是基于原生 ESM 的,qiankun 的 JS 沙箱机制要求子应用必须以 script 标签形式加载,这跟 Vite 的开发模式天然冲突。
如果你正在做微前端项目,我的建议是:主应用可以用 Vite,但子应用最好仔细评估。如果子应用必须用 Vite,可以尝试 vite-plugin-qiankun 这个社区插件,但要做好踩坑的准备。如果你的团队更看重稳定性和成熟的踩坑方案,Webpack 的 Module Federation 会更靠谱。微前端的选型不是喜欢哪个工具的问题,而是架构约束决定了你只能用哪个工具。
6.3 老项目迁移 Vite:收益充分,但也要做好成本预期
老项目从 Webpack 迁移到 Vite,收益确实很诱人:开发启动快、热更新顺滑、依赖体积更小。但迁移过程不是简单的替换配置文件。你可能会遇到:一些 Webpack 特有的全局变量或魔法注释在 Vite 里需要重新实现;某些 loader 在 Vite 中对应的插件还不成熟;某些依赖对原生 ESM 的支持不完整,需要额外配置。
我做一个老项目的迁移建议是这样的:先按路由列表拆分成多个模块,选一个最小可运行的路由模块在 Vite 里跑通,记录所有遇到的兼容性问题,评估解决成本。如果一周内能解决 80% 的兼容问题,就值得全面迁移;如果频繁碰到第三方依赖的兼容性坑,就要考虑还值不值得。迁移期间建议保留 Webpack 构建链路,Vite 只作为开发环境使用,生产环境暂时还是走 Webpack。这样可以先把开发体验提升起来,等 Vite 的生产构建也验证无误后再切换。这个思路风险小、收益明确,我在多个项目里都用过。
7. 常见问题排查速查表:给被报错支配的兄弟们递张地图
构建工具领域的问题,很多时候不是你不会配,而是报错信息太抽象。我整理了日常社区提问率最高的几个问题,做成一个速查表,希望能帮你少走弯路。
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| vite dev 启动极慢 | 依赖预构建范围过大,或者 node_modules 里包数量过多 | 检查 optimizeDeps.include,将常用依赖预先声明;删除 node_modules 重新安装;确保磁盘是 SSD |
| vite build 内存不足,OOM | 项目体积大,Node 默认堆内存不够 | 使用 cross-env NODE_OPTIONS=--max-old-space-size=4096 提高内存上限;或者用 node --max-old-space-size=4096 node_modules/vite/bin/vite.js build |
| webpack 构建内存溢出 | 项目过大,线程开启过多 | 限制 thread-loader 的 workers 数量;关闭过大的 source map;减少 terser 并行数 |
| import.meta.env.VITE_XXX 拿到 undefined | 环境变量没有 VITE_ 前缀 |
环境变量必须以 VITE_ 开头才能暴露给客户端 |
| vite build --mode test 没有读到 .env.test | package.json 命令写错,或模式名不一致 | 检查 scripts 中是否写为 vite build --mode test,并确认文件名为 .env.test |
| vite 启动后在浏览器白屏 | 入口文件路径不对,或 base 配置错误 |
检查 index.html 中 script 标签的 src 是否指向 /src/main.ts;检查 vite.config 的 root 和 base |
| Webpack 修改代码后 HMR 失效 | 配置了错误的 watch 路径,或使用了一些不支持 HMR 的插件 |
检查 watchOptions,关闭不必要的字段监听;开启 optimization.moduleIds: 'deterministic' |
这里还要补充一个很隐蔽的细节:Vite 生产构建用 Rollup,但是 Rollup 在对一些老库的 CJS 依赖处理上,有时候会提示 'default' is not exported by ...。这类问题多数出现在 mix 依赖的版本上,比较直接的方案是使用 vite-plugin-commonjs 或者在 rollupOptions 里配置 output.interop: 'auto'。另外,如果你的项目里有很多用 require() 写的旧 Node 模块,尽量别直接用 Vite 打包,它更适合现代化的 ESM 生态。对于这类场景,先用 Webpack 做一个独立的 UMD 包再去引入,反而是更省事、更稳定的做法。
8. 选型建议:别再纠结,按照这个逻辑做决定
写到这里,我知道很多人最想听的是“到底该选哪个”。我的个人判断其实很直接,总结起来就是三条。
第一,新项目,尤其是以 Vue3 / React 为主、且不需要复杂服务端渲染的项目,直接上 Vite。 小项目、原型、内部系统,Vite 的开发体验是碾压级的,而且生产构建质量经过这两年的迭代已经很稳定。团队里如果要培养新人,Vite 的简洁配置也比 Webpack 那一大堆概念友好得多,学习成本低,上手快。
第二,老项目、大型项目、强定制化构建需求,优先稳住 Webpack。 如果项目已经在 Webpack 上跑了三年五年,线上稳定,无重大构建瓶颈,那么“不迁移”也是一种理性的技术决策。这时候你要做的,是用我上面提到那三板斧去优化 Webpack 配置,把构建时间压下来,把缓存开起来,而不是冒险做一次大换血。工具是服务业务的,业务稳定比构建工具的先进性重要得多。
第三,特殊场景要根据团队短板来选择。 在做微前端时优先考虑 Webpack Module Federation;在做纯静态展示页面时 Vite 是最快捷的;在部署到特殊网络环境、需要完全控制整个构建链路的时候,Webpack 极高的自由度是你最好的后盾。
这几年我越来越觉得,前端所谓“工具选型”,本质上是在和团队的时间、经验、风险承受能力博弈。一套工具替换另一套工具,表面是配置文件变了,背后是整个团队的调试习惯、排错思路、插件选型经验的全部翻新。如果你所在团队对 Webpack 已经熟到骨子里,Vite 再好,也不要为了追新而牺牲团队的确定性;如果你是一个新项目的主导者,Vite 给你带来的开发效率提升,值得你为它投入时间去掌握。
我自己目前的状态是:新项目一律 Vite,老项目按兵不动,逐步把构建链路的通用逻辑抽成公共工具,为将来的风向变化留好余地。最后分享一个我在迁移实践中领悟的小技巧:别把 Webpack 和 Vite 当成非此即彼的对手,在一个仓库里同时保留两套构建配置,开发用 Vite、上线用 Webpack,这类混合模式虽然看着不太优雅,但在过渡期是真的好用。工具是为人服务的,找到最顺手的方式,让团队跑得更快更稳,这才是我们讨论这一切的最终意义。
