esbuild

1. 定义

esbuild 是一个用 Go 写的 JavaScript / TypeScript 打包器 (bundler) + 编译器 (transpiler),把一堆源码模块合并、转译、压缩成浏览器或 Node 能直接跑的产物。 最大卖点:极快——比 webpack / rollup 快 10~100 倍,靠 Go 的原生多线程并行编译实现。

类比:esbuild 之于打包,相当于 ripgrep 之于搜索——同样的活,用编译型语言重写一遍,快出一个数量级。很多现代工具(Vite、tsup)底层就是调 esbuild。

2. esbuild 解决了什么问题

传统 JS 打包器(webpack、rollup)用 JavaScript 自己写,单线程、慢:

问题webpack / rollupesbuild
打包速度大项目冷启动几十秒起通常亚秒级
语言实现JS(单线程、有 GC 开销)Go(原生多线程并行)
TS 转译ts-loader / babel内置,直接吃 .ts
配置复杂度配置项极多、心智负担重API 精简,几个选项就能用

痛点:前端项目越大,webpack 越慢,改一行代码等半天。esbuild 把”转译 + 打包 + 压缩”用 Go 重写并行化,速度碾压。

注意定位:esbuild 追求,牺牲了一部分功能完整度(如复杂的代码分割、类型检查)。它常被当作底层引擎,而非全功能替代 webpack。

3. 核心概念

  • Bundle(打包):把入口文件及其所有 import 依赖递归合并成一个(或几个)输出文件
  • Transpile(转译):把 TS / JSX / 新语法 (ES2022) 降级成目标环境能跑的 JS。注意:esbuild 只做语法转换,不做类型检查(类型错误照样打包成功,类型检查得靠 tsc --noEmit
  • Minify(压缩):删空格、缩短变量名、去死代码,减小体积
  • Loader:告诉 esbuild 怎么处理不同后缀文件(.ts.jsx.css.png 等)
  • Target:输出目标(es2017chrome58node18…),决定语法降级到什么程度
  • Platformbrowser(默认)/ node / neutral,影响模块解析和内置全局变量

4. 常用命令

CLI 基础

esbuild app.ts --bundle --outfile=out.js         # 打包成单文件
esbuild app.ts --bundle --minify --outfile=out.js # 打包 + 压缩
esbuild app.ts --bundle --sourcemap              # 生成 sourcemap
esbuild app.ts --bundle --watch                  # 监听变化自动重打包

常用选项

--bundle                    # 开启打包(否则只转译单文件)
--minify                    # 压缩
--sourcemap                 # 生成 .map 便于调试
--target=es2017             # 语法降级目标
--platform=node             # 面向 Node(默认 browser)
--format=esm                # 输出模块格式:esm / cjs / iife
--outdir=dist               # 输出到目录(多入口时用)
--external:react            # 把某个包排除,不打进产物(运行时再提供)
--loader:.png=dataurl       # 指定某后缀的加载方式

通过 npx 临时用(不全局装)

npx esbuild app.ts --bundle --outfile=out.js

JS API 方式(更常见于脚本 / 构建工具里)

// build.mjs
import * as esbuild from "esbuild";
 
await esbuild.build({
  entryPoints: ["src/app.ts"],
  bundle: true,
  minify: true,
  sourcemap: true,
  target: ["es2020"],
  platform: "browser",
  outfile: "dist/app.js",
});
node build.mjs

5. esbuild vs webpack vs rollup vs Vite 速查

维度webpackrollupesbuildVite
语言JSJSGoJS(底层用 esbuild + rollup)
速度极快快(dev 用 esbuild)
定位全功能应用打包库打包极速转译/打包引擎现代前端开发工具
代码分割基础强(靠 rollup)
TS 类型检查靠 loader靠插件不做不做(需 tsc)
插件生态最丰富丰富较少丰富
典型用途传统大型应用发布 npm 库底层引擎 / 简单打包现代 SPA 开发

关键认知:Vite 开发模式底层就是用 esbuild 做超快转译,生产构建才切到 rollup。esbuild 更多是”被其他工具使用的引擎”。

6. 典型工作流

打包一个 Node CLI 工具成单文件

esbuild src/cli.ts --bundle --platform=node --target=node18 --outfile=dist/cli.js

打包前端库(排除 peer 依赖)

esbuild src/index.ts --bundle --format=esm --external:react --external:react-dom --outfile=dist/index.js

配合类型检查(esbuild 不查类型,得分开做)

tsc --noEmit                 # 先单独做类型检查
esbuild src/app.ts --bundle --outfile=dist/app.js   # 再打包

7. 常见坑

  • 不做类型检查:TS 类型错误 esbuild 照样打包通过。CI 里必须另跑 tsc --noEmit,否则类型 bug 会漏到生产
  • 代码分割能力有限:复杂的 chunk 拆分、动态 import 优化不如 rollup/webpack,做大型应用产物优化时可能不够用
  • CSS / 静态资源处理基础:能处理但功能简单,复杂场景(CSS Modules、图片压缩)要靠插件或换工具
  • --bundle 忘了加:不加只做单文件转译,import 不会被合并进来,运行时报找不到模块
  • --platform 用错:给 Node 打包却用默认 browser,会导致 require/内置模块解析异常
  • 装饰器 / 某些 TS 特性emitDecoratorMetadata 等依赖类型信息的特性 esbuild 不支持,需要用 tsc 或 swc

8. 延伸阅读 / 关联概念

  • Vite — 现代前端构建工具,dev 阶段底层就是 esbuild,最常见的 esbuild 使用场景
  • tsup — 基于 esbuild 的库打包工具,配置更简单,专门打 npm 包
  • swc — 同样用编译型语言(Rust)写的 JS/TS 转译器,和 esbuild 定位相近,是竞品
  • Bun — 内置的 bun build 也是极速打包器,思路类似;见 bun.md
  • rollup / webpack — 功能更全的传统打包器,esbuild 常和它们互补而非完全替代
  • 官方文档:https://esbuild.github.io/