前言
如果只用一句话理解 Webpack、Rollup 和 Vite:
| 工具 | 核心定位 |
|---|---|
| Webpack | 万物皆模块 |
| Rollup | 极致打包库 |
| Vite | 利用浏览器 ESM 的新一代开发工具 |
它们不是简单的“谁替代谁”,而是分别代表了前端工程化的几个阶段:
- Webpack 解决的是:前端所有资源如何统一工程化。
- Rollup 解决的是:库代码如何输出得更干净、更适合分发。
- Vite 解决的是:现代浏览器已经支持 ESM,开发时为什么还要先整体打包。
Webpack:前端工程化的统治者
Webpack 真正伟大的地方,不只是能打包 JavaScript,而是它第一次把前端里的各种资源都纳入了统一的模块系统。
在 Webpack 之前,前端资源常常是割裂的:
1 | JS 是 JS |
Webpack 之后,所有资源都可以被当成模块处理:
1 | import "./index.css"; |
这在当时是一次很大的变化。
Webpack 的核心思想
Webpack 会从入口文件开始,递归分析依赖,构建完整的依赖图。
1 | index.js |
它会把这些模块全部分析出来,再根据配置输出最终 bundle。
Loader 和 Plugin
Webpack 的两个核心扩展机制是 Loader 和 Plugin。
Loader 用来处理“不是 JavaScript 的东西”:
| Loader | 作用 |
|---|---|
| babel-loader | 编译新版本 JavaScript |
| ts-loader | 编译 TypeScript |
| css-loader | 处理 CSS import |
| sass-loader | 编译 Sass |
| file-loader | 处理图片、字体等资源 |
Plugin 用来介入构建生命周期:
- 压缩代码
- 注入 HTML
- 处理环境变量
- 抽离 CSS
- 实现 HMR
- 分析构建产物
Webpack 强就强在:它几乎能处理所有前端工程问题。
Webpack 的问题
Webpack 的核心问题也来自它的架构:开发时通常需要先整体分析、整体打包,再启动 dev server。
1 | 读取入口 |
项目越大,这个过程越重。
尤其当项目里有大量:
node_modules- TypeScript
- Babel 编译
- CSS 预处理
- 大量业务模块
启动和 HMR 都会变慢。
Rollup:库打包之王
Rollup 的定位和 Webpack 不一样。
Webpack 更像是应用构建工具,它想接管整个前端工程。
Rollup 更像是 ESM 优化器,它更关注一件事:
如何生成更干净、更小、更适合发布的库代码。
为什么 Rollup Tree Shaking 强
Rollup 从设计上就围绕 ESModule 展开。
ESModule 是静态模块系统,import 和 export 关系在编译阶段就能确定。
比如:
1 | export const add = () => {}; |
如果使用方只引入:
1 | import { add } from "my-lib"; |
Rollup 可以更容易判断 minus 没有被使用,从而把它删掉。
这就是 Rollup 擅长 Tree Shaking 的根本原因。
Rollup 适合什么
| 场景 | 是否适合 |
|---|---|
| SDK | 适合 |
| npm 工具库 | 适合 |
| UI 组件库 | 适合 |
| 框架源码打包 | 适合 |
| 复杂业务应用 | 不太适合 |
React、Vue、Three.js 等库都使用过 Rollup 或与 Rollup 相关的构建方案,就是因为它的产物更适合分发给别人使用。
为什么应用开发不常直接用 Rollup
Rollup 并不是不能打包应用,而是不擅长做复杂应用开发体验。
它的短板主要在:
- Dev Server 能力不是核心优势
- HMR 体验不如 Vite、Webpack
- 复杂资源处理能力不如 Webpack
- 应用级插件生态不如 Webpack 成熟
所以 Rollup 的最佳位置是库构建,而不是大型业务应用的开发服务器。
Vite:新时代的构建工具
Vite 真正革命的地方是:
开发环境不打包。
这和 Webpack 的思路完全不同。
Webpack 怎么启动
Webpack 开发时通常是:
1 | 先分析全部依赖 |
所以项目越大,启动越慢。
Vite 怎么启动
Vite 利用浏览器原生 ESModule。
1 | import Button from "./Button"; |
浏览器看到这个 import 后,会自己请求 Button 对应的模块。
Vite Dev Server 只需要在浏览器请求某个模块时,按需把它编译成浏览器能执行的 JavaScript。
1 | 浏览器请求模块 |
这就是 Vite 启动快的核心原因:
它不是打包得更快,而是开发时根本不先打包。
Vite 为什么使用 esbuild
浏览器虽然支持 ESM,但并不直接认识所有工程源码。
比如浏览器不认识:
- TypeScript 类型标注
- JSX
- 部分新语法降级
- CommonJS 依赖
所以 Vite 仍然需要编译转换。
这里 esbuild 的作用主要是做高速 transform:
1 | TS → JS |
注意,开发阶段的 esbuild 主要不是在做完整应用打包,而是在做非常快的单文件转换和依赖预构建。
esbuild 为什么快
esbuild 快主要有几个原因:
- 使用 Go 编写,原生并发能力强
- AST 和内存模型高度优化
- 内置常见语法转换,不依赖大量 JavaScript 插件链
- 专注快速转换,不追求覆盖 Babel 那样复杂的全部编译场景
所以它非常适合被 Vite 用作底层编译能力。
Vite HMR 为什么特别快
很多人会把 Vite 快简单理解成:
Vite 快 = esbuild 快。
这个说法只说对了一半。
Vite HMR 快的核心其实是:
开发时不打包,加上模块粒度更新。
Webpack 的 HMR 仍然运行在 bundle 体系里。改一个文件后,Webpack 需要重新计算受影响模块、重新生成相关 chunk,再把更新发送给浏览器。
Vite 则更接近源码模块本身。
如果只改了一个 Button.tsx:
1 | Webpack:改一个文件 → 影响 bundle/chunk → 重新生成更新 |
其它没有变化的模块不需要重新处理。
所以 Vite HMR 快,不是因为“重新打包更快”,而是因为“根本不重新打包”。
node_modules 为什么也变快
传统 Webpack 项目里,node_modules 也会参与大量依赖分析。
Vite 会在第一次启动时做依赖预构建:
1 | CommonJS / 多入口依赖 |
后续启动或更新时,如果依赖没有变化,就可以直接复用缓存。
这也是 Vite 开发体验好的重要原因。
开发和生产构建的区别
Vite 开发环境和生产环境底层策略不同。
开发环境:
1 | 浏览器原生 ESM |
生产环境:
1 | 源码 |
也就是说,现代前端常见组合其实是:
1 | Vite 负责开发体验 |
三者现在的真正定位
| 工具 | 真正定位 |
|---|---|
| Webpack | 老工程生态王,复杂工程仍然强 |
| Rollup | 库、SDK、组件库构建王 |
| Vite | 现代应用开发首选 |
Webpack 没有死。
它在很多场景仍然很强:
- 企业老项目维护
- 高度定制构建
- 复杂插件体系
- Module Federation
- 微前端架构
- 特殊资源处理流程
只是对于大多数现代新项目来说,Vite 的开发体验更好,配置成本更低。
一句话总结
Webpack:
我来接管整个前端世界。
Rollup:
我要生成最干净的 JavaScript。
Vite:
既然浏览器已经支持 ESM,那开发时为什么还要先打包?
Vite HMR 快的本质不是“打包更快”,而是“开发时不打包”。esbuild 的价值,是把每一次按需转换做得足够快。