前端工程化的发展(4):2020+ 新一代工具(Vite / esbuild / SWC)

174 阅读11分钟

2020+ 新一代工具(Vite / esbuild / SWC)

初步认识

为什么会出现新一代工具?

上一阶段(Webpack / Rollup / Parcel)虽然极大提升了前端开发,但随着项目复杂化,痛点越来越突出:

  1. 启动慢
    • Webpack 打包方式是“全量打包”:改一行代码,往往要重新构建整个依赖树。
    • 大型项目动辄几十秒甚至几分钟的冷启动,开发体验差。
  2. 热更新卡顿
    • HMR(热模块替换)本意是提升效率,但在大型项目中,热更新也可能要几秒,严重割裂“即时反馈”的体验。
  3. JavaScript 本身的进化
    • 浏览器原生支持 ES Modules(ESM)越来越好,本地开发时不一定非要“打包”。
    • 但 Webpack 那套依赖转换方式还停留在“打包优先”的思路。

于是,新的工具开始尝试 回到本源
👉 “开发阶段,不打包,直接利用浏览器能力;生产阶段,再做优化打包。”


新一代工具的核心理念

可以归纳为三点:

  1. 开发与生产分离
    • 开发:尽量直接、快速、原生(用 ESM,避免复杂打包)。
    • 生产:才进行优化(Tree-shaking、压缩、代码拆分)。
  2. 极致性能
    • Go / Rust 写构建工具(如 esbuild、SWC),速度提升几十倍。
    • Webpack 的 JS 实现,速度天花板有限。
  3. 面向现代浏览器
    • 不再假设 IE11 一定存在。
    • 利用浏览器原生功能(ESM、HTTP/2 多路复用、原生 import)。

代表工具解析

1. Vite(尤雨溪团队,2020)
  • 思路
    • 开发环境:不打包,直接用浏览器的 ES Modules。修改代码 → 浏览器直接重新请求该模块 → 秒级反馈。
    • 生产环境:借助 Rollup 打包优化。
  • 特点
    • 极快冷启动(不需要打包整个项目)。
    • 局部更新快(改一个模块,就只重新加载那部分)。
    • 天然支持 Vue、React 等现代框架。
  • 影响力
    • 已经取代 Webpack,成为 Vue 官方推荐工具。
    • React 社区也大量采用(Vite + SWC)。

2. esbuild(Evan Wallace,2020)
  • 背景
    • 作者是 Figma 联合创始人之一。Figma 本身是对性能要求极高的应用。
    • esbuild 用 Go 语言写,性能比 Webpack 提升 10~100 倍
  • 特点
    • 超高速打包和压缩。
    • 支持 Tree-shaking、代码分割。
    • 许多工具(包括 Vite)内部都调用 esbuild 来处理依赖。
  • 定位
    • 更像是“底层引擎”,不直接和开发者交互,而是被集成到其他工具中。

3. SWC(Speedy Web Compiler,Rust 实现)
  • 定位
    • 类似 Babel + Terser(JS 压缩)的替代品。
    • Rust 编写,速度极快。
  • 应用
    • Next.js 已经全面替换 Babel,使用 SWC 来编译和压缩代码。
    • 未来也可能成为更多工具的默认“编译引擎”。

对比总结:旧 vs 新

维度Webpack / RollupVite / esbuild / SWC
核心思路打包优先,开发和生产一致开发不打包,生产再优化
技术栈JS 实现Go / Rust 实现
性能冷启动慢,热更新延迟冷启动秒开,热更新即时
面向环境兼容各种浏览器(包括旧版)偏向现代浏览器(ESM 原生支持)
定位全功能打包器新一代编译/构建引擎

整体演进的必然性

  1. 早期(Grunt / Gulp):任务自动化,减少手工。
  2. 打包时代(Webpack):为了解决模块化和兼容性问题。
  3. 新一代(Vite / esbuild / SWC):回到本源,充分利用浏览器与现代语言的性能优势。

总结:
👉 “新一代工具的本质,是在现代浏览器环境下,把开发体验做到极致(快、简洁、直接),同时在生产环境仍保留传统优化手段。”


Vite时期 的开发环境 vs 生产环境

问题背景:Webpack 的局限

Webpack 时代(2015+),虽然区分了 Dev / Prod 两个模式,但存在两个痛点:

  1. 开发环境慢
    • Webpack 开发时也要“打包”(即便不开压缩、不开优化),仍然要把所有依赖走一遍 Loader、构建 Bundle。
    • 项目几百个模块时,冷启动慢、改代码再热更新也慢
  2. 生产环境和开发环境差距大
    • Dev 下是“一个 DevServer 提供 HMR 的 Bundle”,
    • Prod 下是“优化压缩后的多 Bundle 文件”。
    • 两个模式产物差别大,导致“线上 bug 无法在本地复现”的情况时有发生。

👉 所以当时人们就在思考:能不能让开发环境和生产环境快,又更接近?

**注意:**什么是让开发环境和生产环境更接近?这里的接近指的开发时代码和浏览器运行代码的接近,在Webpack时浏览器运行的就是打包后的代码,与开发时代码有很大差异。


Vite 的核心创新

Vite(2020+)的设计可以总结为一句话:

开发时不打包,生产时再优化


Vite 的两套模式

1. 开发环境(Dev)

目标:极致快的开发体验
核心原理:

  • 原生 ESM 支持
    现代浏览器(Chrome/Edge/Firefox/Safari)已经能原生识别 import
    👉 开发时,Vite 不再打包,而是直接把源码交给浏览器:
// 你写的
import { add } from './utils/math.js'

👉 浏览器自己去请求 /src/utils/math.js,Vite 只做 按需的转换(Transform)

- `.ts → .js`
- `.vue → JS + CSS`
- `.scss → CSS`
- 图片/字体 → URL
  • 即时启动:冷启动时不再“全量打包”,而是只在请求到某个模块时才进行转换。
  • HMR 极速更新:修改一个 Vue 组件,只需重新编译那一个文件,毫秒级反馈。

对比 Webpack:
Webpack DevServer:启动 → 打包所有文件 → 浏览器请求的其实是一个大的 bundle。
Vite DevServer:启动 → 几乎零等待 → 浏览器请求哪个文件才编译哪个文件。


2. 生产环境(Build)

目标:最优产物,适合上线
核心原理:

  • Rollup 打包:Vite 生产构建仍然使用 Rollup,原因是:
    1. 线上不能让用户发 300 个 HTTP 请求去拿 300 个模块 → 要合并、拆分成更合理的 chunk
    2. 要进行 Tree-shaking、压缩、代码分割 等优化。
  • 资源优化
    • JS 压缩(Terser/Esbuild)
    • CSS 提取和压缩
    • 图片/字体文件 hash 命名,结合浏览器缓存
  • 兼容性:有些老浏览器不能跑原生 ESM,需要打包成兼容的产物。

对比总结(Webpack vs Vite)

维度Webpack DevVite DevWebpack ProdVite Prod
启动速度慢,需要打包快,直接走原生 ESM中等偏慢Rollup 优化产物
HMR秒级,依赖全局构建毫秒级,局部转换不支持不支持
调试体验Bundle 代码,不完全等同源码源码即模块,调试友好优化后代码难调试优化后代码难调试
产物优化Dev 模式无优化Dev 不需要产物体积优化(Tree-shaking、压缩)同样优化,但更高效

总结:
👉 Webpack 的 Dev 还是“打包”,Vite 的 Dev 是“原生模块直出”。
👉 Vite 的 Prod 依旧要打包,但借助 Rollup,优化得更极致。


工程化意义

Vite 带来了一个“前端构建的分工明确”思想:

  • 开发体验用 ESM → 快速反馈
  • 上线体验用 Rollup → 最优产物

它让“开发 vs 上线”这两个环境的差异 合理化,既兼顾了开发效率,又兼顾了生产性能。


打包是否还必要?

前置思考

前面讲到随着浏览器原生支持 ES Modules(ESM)越来越好,开发时可以不打包,且开发时访问前端项目也很流畅,现代浏览器可以按需加载模块,运行也很流畅,可见在本地运行时性能上并没有问题,抛开兼容性不谈,既然已经能够流畅运行,打包是否还必要?


开发时“流畅”,为什么?

  • 现代浏览器能识别 ESM,遇到 import 就会发起请求,按需加载依赖。
  • HTTP/2/3 多路复用,大量小文件请求开销降低。
  • 在开发机(本地环境)通常只有一个人访问,网络条件接近 局域网级别,延迟很低。
    👉 所以你感觉性能很好,毫无压力。

生产环境的问题

一旦项目上线,面对的环境就完全不同:

(1)请求数量和延迟问题
  • 一个真实项目可能有几百、几千个模块(尤其是 Vue/React + 各种依赖)。
  • 浏览器会为每个 import 发起一次请求:
    • 即使 HTTP/2 支持并发,TLS 握手、Header 传输、服务器响应延迟等依然存在。
    • 在移动网络(4G/5G、弱网环境)里,几百个请求会严重拖慢首屏加载。

例子:

  • 本地开发:300 个模块,0ms 延迟 → 几乎瞬间。
  • 生产环境:300 个请求 × 50ms 延迟 = 15 秒光是 RTT(往返延迟),首屏卡死。

(2)缓存优化
  • 如果不打包,300 个文件每次都要单独请求、单独协商缓存。
  • 打包后:
    • 核心代码打成 app.js,第三方库打成 vendor.js
    • 只有变更的 chunk 会重新下载 → 缓存利用率高。

(3)Tree Shaking / Dead Code Elimination
  • 打包工具能静态分析 import/export,只保留用到的代码。
  • 如果直接让浏览器加载 lodash-es,那就是完整包(几百 KB)。
  • 但通过打包 → 最终可能只保留 cloneDeep,大小降到几十 KB。

(4)非 JS 资源处理
  • 浏览器不认识 .vue.ts.scss、图片 import。
  • 必须通过打包工具把它们转换成 JS/CSS/HTML。
  • 没有打包,这些文件压根没法跑在生产环境。

(5)安全 & 部署问题
  • 打包过程还能自动插入 polyfill,屏蔽不同浏览器的差异。
  • 输出产物经过压缩、混淆,能保护源码逻辑,不会直接暴露业务代码。
  • 静态资源加 hash,防止 CDN 缓存不一致。

按需加载能否成为打包的必要理由

前置思考

如果仔细阅读上面内容,仔细思考就会发现,其中依然有些矛盾点,令人困惑:现代浏览器能识别 ESM,遇到 import 就会发起请求,按需加载依赖”,本地不打包的时候不就已经做到了按需加载吗?那么我们在上面为什么还把上线后“如果不打包,300 个文件每次都要单独请求、单独协商缓存”作为需要打包的理由?明明不打包现代浏览器也按需加载。


首先我们的疑惑是正确的:不打包时,现代浏览器 ESM 本身就是按需加载
那为什么我还说“300 个文件会有问题”?关键在于:

  • 本地开发:所有文件都在你的硬盘里,Vite Dev Server 直接用 HTTP 把文件吐给浏览器 → 网络延迟几乎为零。
  • 线上生产
    • 每个 import 都是一次 HTTP 请求 → 请求头、握手、TLS、服务器响应时间,都会累计。
    • 虽然 HTTP/2 可以多路复用,但并不是“零开销”。几百个请求依然比 2~3 个请求慢得多。

👉 结论

  • 不打包:按需没错,但请求太多,首屏加载慢。
  • 打包:把 300 个模块打成 2~3 个大文件 → 大幅减少请求次数。

所以并不是“按需”错了,而是“请求数 + 网络环境”导致上线体验很差。


非 JS 资源处理能否成为打包的必要理由?

前置思考

对于非JS资源处理,前面我们说“浏览器不认识 .vue、.ts、.scss、图片 import。 必须通过打包工具把它们转换成 JS/CSS/HTML”,但同时前面又说,现代浏览器可以不打包,也能运行,那么“对非JS资源处理”也不能成为一定要打包的理由吧?


这里要区分两个层次:

(1)现代浏览器原生 ESM 确实只支持 JS 模块

  • .vue.ts.scss → 浏览器不认识,不能直接运行。
  • 你在本地开发能跑起来,是因为 Vite/Webpack Dev Server 在背后帮你做了 “即时转换(On-the-fly transform)”
    • .ts → Babel/tsc 转成 JS。
    • .scss → Sass 编译成 CSS,再注入。
    • .vue → Vue 编译器拆成 JS + CSS。
  • 这些都不是浏览器原生完成的,而是 Node.js 构建工具临时帮你做的。

(2)所以不打包也能跑 → 但前提是 dev server 做了伪打包

比如 Vite:

  • 开发时:它把 .vue 转换成 JS,直接发给浏览器(虽然没打包,但已经“编译”了)。
  • 生产时:必须提前把这些转换过的内容,合并、压缩、分 chunk,输出静态产物,才能上线。

👉 结论

  • 不打包时你看到的“也能跑” → 其实背后有 实时编译,只是没有合并、优化。
  • 真正裸奔的浏览器(关掉 Vite/webpack-dev-server),你把 .vue.ts 丢给它,它完全跑不起来。

总结

  • 按需加载问题
    • 本地 OK(几乎零延迟)。
    • 线上不 OK(请求数太多,延迟叠加)。
    • → 打包能减少请求数。
  • 非 JS 资源问题
    • 浏览器只能原生理解 JS 模块。
    • .ts/.vue/.scss 等都要先转译成 JS/CSS/HTML → 这个动作即使不打包,也必须做。
    • → 打包不仅转译,还进一步合并优化,才能真正上线。
  • 开发时:追求“快”,ESM 原生支持 + Vite → 不打包,随改随生效。
  • 生产时:追求“稳 + 省” → 打包,解决请求数量、性能优化、缓存、兼容、非 JS 资源转换。

👉 所以,现代前端的最佳实践是:开发时不打包,生产时必须打包。