编程 用 Go 重写打包器:esbuild 为什么不用缓存也能比 webpack 快 10–100 倍

2026-09-12 09:50:48

用 Go 重写打包器:esbuild 为什么不用缓存也能比 webpack 快 10–100 倍

项目信息:

  • GitHub:
  • 官网:
  • 文档(Getting Started):
  • Go 官网:

1. 为什么需要 esbuild

一个常见场景:中型前端项目,几百个文件,用 webpack 打包。改一行代码,等 30 秒才能看到效果。

常见的优化手段挨个试过:

  • 开缓存:有点用,但第一次还是慢
  • 配多线程:有点用,但配置复杂
  • 增量编译:有点用,但有时候不准
  • 换 SSD:有点用,但治标不治本

最后会发现,瓶颈在 webpack 本身。webpack 是 JavaScript 写的,单线程,解析、转换、打包都是串行的,项目一大就慢得受不了。

esbuild 的思路是:用 Go 重写,从头优化。

作者的原话:

Our current build tools for the web are 10-100x slower than they could be.

也就是说,现在的 Web 构建工具比它们应有的速度慢 10–100 倍,esbuild 的目标就是把这个速度提上来。

2. esbuild 是什么

esbuild 是一个用 Go 写的 Web 打包工具。官方的描述:

An extremely fast bundler for the web.

具体来说:

  • 打包速度极快
  • 内置 JavaScript、CSS、TypeScript、JSX 支持
  • 提供 CLI、JavaScript、Go 三种 API
  • 支持 ESM 和 CommonJS 模块

核心定位

esbuild 不是 webpack 的插件,而是替代品。它做的事情和 webpack 一样:

  1. 解析源代码
  2. 处理依赖关系
  3. 转换语法(TypeScript → JavaScript)
  4. 打包输出
  5. 压缩优化

区别在于速度完全不是一个量级。

3. 为什么这么快

原因一:Go 写的

Go 是编译型语言,性能比 JavaScript 好很多。

  • webpack:JavaScript 写的,靠 V8 引擎执行
  • esbuild:Go 写的,编译成原生二进制

性能差距在 10 倍以上。

原因二:并行处理

esbuild 充分利用多核 CPU。

  • webpack:单线程,串行处理
  • esbuild:多线程,并行解析、转换、打包

CPU 有几个核,esbuild 就用几个核。

原因三:内存优化

esbuild 尽量减少内存分配和拷贝。

  • webpack:大量字符串操作,内存拷贝多
  • esbuild:使用字节切片,减少拷贝

原因四:不用缓存

esbuild 快到不需要缓存。

  • webpack:必须开缓存,否则每次全量编译
  • esbuild:不用缓存,直接编译就比 webpack 快

这意味着不需要清理缓存,不用担心缓存失效,也不用管缓存策略。

4. 安装和使用

方式一:npm 安装

npm install esbuild --save-dev

方式二:全局安装

npm install -g esbuild

方式三:Go 安装

go install github.com/evanw/esbuild/cmd/esbuild@latest

基本使用

1. 命令行

# 打包 JavaScript
esbuild src/index.js --bundle --outfile=dist/index.js

# 打包 TypeScript
esbuild src/index.ts --bundle --outfile=dist/index.js

# 打包 CSS
esbuild src/index.css --bundle --outfile=dist/index.css

# 压缩
esbuild src/index.js --bundle --minify --outfile=dist/index.js

# 生成 source map
esbuild src/index.js --bundle --sourcemap --outfile=dist/index.js

2. JavaScript API

const esbuild = require('esbuild');

esbuild.build({
entryPoints: ['src/index.js'],
bundle: true,
outfile: 'dist/index.js',
minify: true,
sourcemap: true,
}).catch(() => process.exit(1));

3. Go API

package main

import (
"github.com/evanw/esbuild/pkg/api"
)

func main() {
result := api.Build(api.BuildOptions{
EntryPoints: []string{"src/index.js"},
Bundle:      true,
Outfile:     "dist/index.js",
Minify:      true,
Sourcemap:   api.SourceMapLinked,
})

if len(result.Errors) > 0 {
// 处理错误
}
}

4. 开发服务器

esbuild src/index.js --bundle --outdir=dist --serve=8000 --servedir=www

5. Watch 模式

esbuild src/index.js --bundle --outfile=dist/index.js --watch

5. 核心功能

功能一:模块打包

同时支持 ESM 和 CommonJS,esbuild 会自动转换:

// ESM
import { foo } from './foo.js';

// CommonJS
const bar = require('./bar.js');

功能二:TypeScript 支持

直接编译 TypeScript,不需要 tsc:

esbuild src/index.ts --bundle --outfile=dist/index.js

支持:

  • 类型注解(编译时删除)
  • 接口、枚举
  • 装饰器(实验性)
  • tsconfig.json 配置

功能三:JSX 支持

直接编译 JSX,不需要 Babel,支持 React、Preact 等:

esbuild src/App.jsx --bundle --outfile=dist/App.js

功能四:CSS 支持

打包 CSS,支持 CSS Modules:

esbuild src/index.css --bundle --outfile=dist/index.css

支持:

  • @import
  • url() 重写
  • CSS Modules
  • 自动前缀(实验性)

功能五:Tree Shaking

自动删除未使用的代码:

// foo.js
export function used() { return 'used'; }
export function unused() { return 'unused'; }

// index.js
import { used } from './foo.js';
console.log(used());

打包后 unused 函数会被删除。

功能六:压缩

内置压缩器,可以替代 terser:

esbuild src/index.js --bundle --minify --outfile=dist/index.js

压缩内容包括变量名缩短、空白删除、语句优化。

6. 实战场景

场景一:替代 webpack

问题是 webpack 打包太慢。配置如下:

// esbuild.config.js
const esbuild = require('esbuild');

esbuild.build({
entryPoints: ['src/index.js'],
bundle: true,
outfile: 'dist/index.js',
minify: true,
sourcemap: true,
target: ['es2020'],
loader: {
'.js': 'jsx',
'.ts': 'tsx',
'.png': 'dataurl',
'.svg': 'dataurl',
},
define: {
'process.env.NODE_ENV': '"production"',
},
}).catch(() => process.exit(1));
node esbuild.config.js

打包时间从 30 秒降到 1 秒。

场景二:TypeScript 编译

问题是 tsc 编译太慢:

# 单文件
esbuild src/index.ts --outfile=dist/index.js

# 多文件
esbuild "src/**/*.ts" --outdir=dist

# Watch 模式
esbuild src/index.ts --outfile=dist/index.js --watch

编译时间从 10 秒降到 0.5 秒。

场景三:开发服务器

需要热更新时:

esbuild src/index.js --bundle --outdir=dist --serve=8000 --servedir=public

浏览器访问 http://localhost:8000,自动加载打包后的代码。改代码后自动刷新,毫秒级响应。

场景四:插件扩展

需要自定义处理逻辑时,可以写插件:

const esbuild = require('esbuild');
const { execSync } = require('child_process');

// 自定义插件:运行 Tailwind CSS
const tailwindPlugin = {
name: 'tailwind',
setup(build) {
build.onEnd(() => {
execSync('tailwindcss -i src/index.css -o dist/index.css');
});
},
};

esbuild.build({
entryPoints: ['src/index.js'],
bundle: true,
outfile: 'dist/index.js',
plugins: [tailwindPlugin],
});

这样可以在 esbuild 流程中集成其他工具。

7. 设计亮点

Go 实现:原生二进制,性能优秀;跨平台,编译一次到处运行;并发支持好。

单二进制:esbuild 是一个单独的二进制文件,没有依赖。安装简单,启动快,不需要 Node.js 环境。

多语言 API:提供 CLI、JavaScript、Go 三种 API,易于集成,适配不同场景。

内置功能:TypeScript、JSX、CSS 都内置支持,不需要额外工具,配置简单,构建链短。

插件系统:支持自定义插件,可扩展,有社区贡献,能适配特殊需求。

8. 和类似方案对比

vs. webpack

维度esbuildwebpack
语言GoJavaScript
速度极快
生态较小丰富
插件实验性成熟
配置简单复杂

结论:esbuild 快,webpack 生态好。

vs. Rollup

维度esbuildRollup
速度极快中等
定位应用
Tree Shaking更好
代码分割支持有限

结论:esbuild 快,Rollup 打包库更好。

vs. Vite

维度esbuildVite
定位打包工具构建框架
速度极快
开发体验CLI完整方案
生产构建esbuildRollup

结论:esbuild 是工具,Vite 是框架。Vite 生产构建用的是 Rollup,不是 esbuild。

9. 局限性

  1. 生态不成熟:插件数量不如 webpack
  2. 不支持 HMR:需要自己实现热更新
  3. 代码分割有限:不如 webpack 灵活
  4. 某些特性是实验性的:CSS 自动前缀、装饰器等
  5. 调试困难:报错信息不如 webpack 清晰
  6. 不适合复杂场景:微前端、模块联邦等

10. 总结

从 esbuild 这个项目能看出几个趋势:

  1. 性能是刚需,开发者受够了慢速构建
  2. Go 可以写前端工具,不只 JavaScript 能写前端
  3. 单二进制是趋势:简单、快速、无依赖
  4. 内置功能受欢迎,不需要一堆配置
  5. 替代方案有市场,webpack 不是唯一选择

esbuild 生态不如 webpack,但它解决的是真问题。如果受够了 webpack 的慢速,想试更快的构建工具,esbuild 值得一试。


参考资料

  • esbuild GitHub:
  • esbuild 官网:
  • esbuild 文档:
  • Go 语言:
复制全文 生成海报 esbuild Go webpack 构建工具 前端工程化

推荐文章

程序员茄子在线接单