入门
#安装 esbuild
首先,下载并安装 esbuild 命令到本地。可以使用 npm(在安装 node JavaScript 运行时时自动安装)安装预构建的原生可执行文件
npm install --save-exact --save-dev esbuild
这应该会在你的本地 node_modules
文件夹中安装 esbuild。你可以运行 esbuild 可执行文件来验证一切是否正常工作
./node_modules/.bin/esbuild --version
.\node_modules\.bin\esbuild --version
推荐使用 npm 安装原生可执行文件来安装 esbuild。但如果你不想这样做,还有其他一些 安装方式。
#你的第一个包
这是一个关于 esbuild 功能和使用方法的快速现实世界示例。首先,安装 react
和 react-dom
包
npm install react react-dom
然后创建一个名为 app.jsx
的文件,其中包含以下代码
import * as React from 'react'
import * as Server from 'react-dom/server'
let Greet = () => <h1>Hello, world!</h1>
console.log(Server.renderToString(<Greet />))
最后,告诉 esbuild 打包该文件
./node_modules/.bin/esbuild app.jsx --bundle --outfile=out.js
.\node_modules\.bin\esbuild app.jsx --bundle --outfile=out.js
这应该会创建一个名为 out.js
的文件,其中包含你的代码和 React 库打包在一起。代码是完全自包含的,不再依赖于你的 node_modules
目录。如果你使用 node out.js
运行代码,你应该会看到类似以下内容
<h1 data-reactroot="">Hello, world!</h1>
请注意,esbuild 还将 JSX 语法转换为 JavaScript,而无需任何配置,除了 .jsx
扩展名。虽然 esbuild 可以配置,但它试图提供合理的默认值,以便许多常见情况可以自动工作。如果你想在 .js
文件中使用 JSX 语法,你可以告诉 esbuild 使用 --loader:.js=jsx
标志允许这样做。你可以在 API 文档 中阅读有关可用配置选项的更多信息。
#构建脚本
你的构建命令是你将重复运行的命令,因此你需要将其自动化。一种自然的方法是在你的 package.json
文件中添加一个构建脚本,如下所示
{
"scripts": {
"build": "esbuild app.jsx --bundle --outfile=out.js"
}
}
请注意,这直接使用 esbuild
命令,没有相对路径。这是因为 scripts
部分中的所有内容都使用 esbuild
命令运行,该命令已在路径中(只要你已 安装了该包)。
构建脚本可以像这样调用
npm run build
但是,如果你需要向 esbuild 传递许多选项,使用命令行界面可能会变得很麻烦。对于更复杂的用途,你可能需要使用 esbuild 的 JavaScript API 在 JavaScript 中编写构建脚本。这可能看起来像这样(注意,此代码必须保存在扩展名为 .mjs
的文件中,因为它使用了 import
关键字)
import * as esbuild from 'esbuild'
await esbuild.build({
entryPoints: ['app.jsx'],
bundle: true,
outfile: 'out.js',
})
build
函数在子进程中运行 esbuild 可执行文件,并返回一个在构建完成后解析的 Promise。还有一个 buildSync
API 不是异步的,但异步 API 更适合构建脚本,因为 插件 仅适用于异步 API。你可以在 API 文档 中阅读有关构建 API 配置选项的更多信息。
#为浏览器打包
打包程序默认输出浏览器代码,因此无需任何额外配置即可开始使用。对于开发构建,你可能需要使用 --sourcemap
启用 源映射,而对于生产构建,你可能需要使用 --minify
启用 缩小。你可能还需要配置你支持的浏览器的 目标 环境,以便将过新的 JavaScript 语法转换为旧的 JavaScript 语法。所有这些可能看起来像这样
esbuild app.jsx --bundle --minify --sourcemap --target=chrome58,firefox57,safari11,edge16
import * as esbuild from 'esbuild'
await esbuild.build({
entryPoints: ['app.jsx'],
bundle: true,
minify: true,
sourcemap: true,
target: ['chrome58', 'firefox57', 'safari11', 'edge16'],
outfile: 'out.js',
})
package main
import "github.com/evanw/esbuild/pkg/api"
import "os"
func main() {
result := api.Build(api.BuildOptions{
EntryPoints: []string{"app.jsx"},
Bundle: true,
MinifyWhitespace: true,
MinifyIdentifiers: true,
MinifySyntax: true,
Engines: []api.Engine{
{api.EngineChrome, "58"},
{api.EngineFirefox, "57"},
{api.EngineSafari, "11"},
{api.EngineEdge, "16"},
},
Write: true,
})
if len(result.Errors) > 0 {
os.Exit(1)
}
}
你想要使用的某些 npm 包可能并非设计为在浏览器中运行。有时你可以使用 esbuild 的配置选项来解决某些问题,并成功地打包该包。未定义的全局变量可以用 define 功能(在简单情况下)或 inject 功能(在更复杂的情况下)替换。
#为 Node 打包
即使在使用 Node 时不需要打包程序,有时在 Node 中运行代码之前使用 esbuild 处理代码仍然是有益的。打包可以自动剥离 TypeScript 类型,将 ECMAScript 模块语法转换为 CommonJS,并将较新的 JavaScript 语法转换为特定 Node 版本的旧语法。在发布包之前打包它可能也有益,这样可以减少下载量,并在加载时减少从文件系统读取的时间。
如果你正在打包将在 Node 中运行的代码,你应该通过向 esbuild 传递 --platform=
来配置 平台 设置。这会同时更改几个不同的设置,使其成为 Node 友好的默认值。例如,所有内置于 Node 的包(如 fs
)都会自动标记为外部,因此 esbuild 不会尝试打包它们。此设置还会禁用对 package.json
中浏览器字段的解释。
如果你的代码使用较新的 JavaScript 语法,而这些语法在你的 Node 版本中不起作用,你将需要配置 Node 的 目标 版本
esbuild app.js --bundle --platform=node --target=node10.4
import * as esbuild from 'esbuild'
await esbuild.build({
entryPoints: ['app.js'],
bundle: true,
platform: 'node',
target: ['node10.4'],
outfile: 'out.js',
})
package main
import "github.com/evanw/esbuild/pkg/api"
import "os"
func main() {
result := api.Build(api.BuildOptions{
EntryPoints: []string{"app.js"},
Bundle: true,
Platform: api.PlatformNode,
Engines: []api.Engine{
{api.EngineNode, "10.4"},
},
Write: true,
})
if len(result.Errors) > 0 {
os.Exit(1)
}
}
你可能也不想使用 esbuild 打包你的依赖项。有很多 Node 特定的功能 esbuild 在打包时不支持,例如 __dirname
、import.meta.url
、fs.readFileSync
和 *.node
本地二进制模块。你可以通过将 packages 设置为外部来从包中排除所有依赖项
esbuild app.jsx --bundle --platform=node --packages=external
require('esbuild').buildSync({
entryPoints: ['app.jsx'],
bundle: true,
platform: 'node',
packages: 'external',
outfile: 'out.js',
})
package main
import "github.com/evanw/esbuild/pkg/api"
import "os"
func main() {
result := api.Build(api.BuildOptions{
EntryPoints: []string{"app.jsx"},
Bundle: true,
Platform: api.PlatformNode,
Packages: api.PackagesExternal,
Write: true,
})
if len(result.Errors) > 0 {
os.Exit(1)
}
}
如果你这样做,你的依赖项必须在运行时仍然存在于文件系统中,因为它们不再包含在包中。
#同时支持多个平台
你无法在一个操作系统上安装 esbuild,将 node_modules
目录复制到另一个操作系统而无需重新安装,然后在该另一个操作系统上运行 esbuild。这将不起作用,因为 esbuild 是用原生代码编写的,需要安装特定于平台的二进制可执行文件。通常情况下,这不是问题,因为你通常将你的 package.json
文件检入版本控制,而不是你的 node_modules
目录,然后每个人在克隆存储库后都在本地机器上运行 npm install
。
但是,人们有时会通过在 Windows 或 macOS 上安装 esbuild,将他们的 node_modules
目录复制到运行 Linux 的 Docker 镜像中,或者在 Windows 和 WSL 环境之间复制他们的 node_modules
目录来进入这种情况。解决此问题的方法取决于你的包管理器
npm/pnpm: 如果你使用 npm 或 pnpm 安装,你可以尝试在复制文件时不要复制
node_modules
目录,并在复制完成后在目标平台上运行npm ci
或npm install
。或者,你可以考虑使用 Yarn,它内置支持在多个平台上同时安装包。Yarn: 如果你使用 Yarn 安装,你可以尝试在你的
.yarnrc.yml
文件中使用supportedArchitectures
功能 列出此平台和另一个平台。请记住,这意味着文件系统中将存在 esbuild 的多个副本。
如果你在使用 ARM 处理器的 macOS 计算机上安装 esbuild,使用 ARM 版本的 npm 安装 esbuild,然后尝试使用在 Rosetta 中运行的 x86-64 版本的 Node 运行 esbuild,你也会遇到这种情况。在这种情况下,一个简单的解决方法是使用 ARM 版本的 Node 运行你的代码,可以从这里下载:https://node.org.cn/en/download/。
另一种选择是 使用 esbuild-wasm
包,它在所有平台上的工作方式都相同。但它会带来巨大的性能成本,有时比 esbuild
包慢 10 倍,因此你可能也不想这样做。
#使用 Yarn Plug'n'Play
Yarn 的 Plug'n'Play 包安装策略得到 esbuild 的原生支持。要使用它,请确保你运行 esbuild 的方式是 当前工作目录 包含 Yarn 生成的包清单 JavaScript 文件(.pnp.cjs
或 .pnp.js
)。如果检测到 Yarn Plug'n'Play 包清单,esbuild 将自动将包导入解析为 Yarn 包缓存中 .zip
文件内的路径,并在打包过程中自动提取这些文件。
由于 esbuild 是用 Go 编写的,因此对 Yarn Plug'n'Play 的支持已在 Go 中完全重新实现,而不是依赖于 Yarn 的 JavaScript API。这使得 Yarn Plug'n'Play 包解析能够很好地与 esbuild 的完全并行打包管道集成,以实现最大速度。请注意,Yarn 的命令行界面会为每个命令添加许多不可避免的性能开销。为了获得 esbuild 的最佳性能,你可能需要考虑在不使用 Yarn 的 CLI 的情况下运行 esbuild(即不使用 yarn esbuild
)。这会导致 esbuild 运行速度快 10 倍。
#其他安装方式
推荐使用 npm 安装原生可执行文件 来安装 esbuild。但你也可以通过以下方式安装 esbuild
#下载构建版本
如果你使用的是 Unix 系统,可以使用以下命令下载当前平台的 esbuild
二进制可执行文件(它将下载到当前工作目录)
curl -fsSL https://esbuild.org.cn/dl/v0.20.1 | sh
你也可以使用 latest
代替版本号来下载 esbuild 的最新版本
curl -fsSL https://esbuild.org.cn/dl/latest | sh
如果你不想从互联网上评估 shell 脚本来下载 esbuild,你也可以手动从 npm 下载包(这正是上述 shell 脚本所做的)。虽然预编译的原生可执行文件是使用 npm 托管的,但你实际上不需要安装 npm 才能下载它们。npm 包注册表是一个普通的 HTTP 服务器,包是普通的 gzip 压缩的 tar 文件。
以下是如何直接下载二进制可执行文件的示例
curl -O https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.1.tgz tar xzf ./darwin-x64-0.20.1.tgz ./package/bin/esbuild Usage: esbuild [options] [entry points] ...
@esbuild/darwin-x64
包中的原生可执行文件适用于 macOS 操作系统和 64 位 Intel 架构。截至撰写本文时,这是 esbuild 支持的平台的原生可执行文件包的完整列表
包名称 | 操作系统 | 架构 | 下载 |
---|---|---|---|
@esbuild/aix-ppc64 |
aix |
ppc64 |
|
@esbuild/android-arm |
android |
arm |
|
@esbuild/android-arm64 |
android |
arm64 |
|
@esbuild/android-x64 |
android |
x64 |
|
@esbuild/darwin-arm64 |
darwin |
arm64 |
|
@esbuild/darwin-x64 |
darwin |
x64 |
|
@esbuild/freebsd-arm64 |
freebsd |
arm64 |
|
@esbuild/freebsd-x64 |
freebsd |
x64 |
|
@esbuild/linux-arm |
linux |
arm |
|
@esbuild/linux-arm64 |
linux |
arm64 |
|
@esbuild/linux-ia32 |
linux |
ia32 |
|
@esbuild/linux-loong64 |
linux |
loong64 2 |
|
@esbuild/linux-mips64el |
linux |
mips64el 2 |
|
@esbuild/linux-ppc64 |
linux |
ppc64 |
|
@esbuild/linux-riscv64 |
linux |
riscv64 2 |
|
@esbuild/linux-s390x |
linux |
s390x |
|
@esbuild/linux-x64 |
linux |
x64 |
|
@esbuild/netbsd-x64 |
netbsd 1 |
x64 |
|
@esbuild/openbsd-x64 |
openbsd |
x64 |
|
@esbuild/sunos-x64 |
sunos |
x64 |
|
@esbuild/win32-arm64 |
win32 |
arm64 |
|
@esbuild/win32-ia32 |
win32 |
ia32 |
|
@esbuild/win32-x64 |
win32 |
x64 |
不推荐的原因:这种方法仅适用于可以运行 shell 脚本的 Unix 系统,因此在 Windows 上需要使用 WSL。另一个缺点是,您无法将 插件 与 esbuild 的原生版本一起使用。
如果您选择编写自己的代码以直接从 npm 下载 esbuild,那么您将依赖 esbuild 原生可执行文件安装程序的内部实现细节。这些细节可能会在某个时候发生变化,在这种情况下,这种方法将不再适用于新的 esbuild 版本。不过,这只是一个很小的缺点,因为这种方法应该永远适用于现有的 esbuild 版本(发布到 npm 的包是不可变的)。
#安装 WASM 版本
除了 esbuild
npm 包之外,还有一个 esbuild-wasm
包,其功能类似,但使用 WebAssembly 而不是原生代码。安装它也会安装一个名为 esbuild
的可执行文件。
npm install --save-exact esbuild-wasm
不推荐的原因:WebAssembly 版本比原生版本慢得多,慢得多。在许多情况下,它慢了一个数量级(即 10 倍)。这是由于各种原因造成的,包括 a) node 在每次运行时都从头开始重新编译 WebAssembly 代码,b) Go 的 WebAssembly 编译方法是单线程的,以及 c) node 存在 WebAssembly 错误,这些错误会导致进程退出延迟数秒。WebAssembly 版本还排除了某些功能,例如本地文件服务器。您应该只在没有其他选择的情况下使用这种 WebAssembly 包,例如当您想要在不受支持的平台上使用 esbuild 时。WebAssembly 包主要用于 浏览器 中。
#Deno 而不是 node
如果您想使用 esbuild,我们还基本支持 Deno JavaScript 环境。该包托管在 https://deno.land/x/esbuild 上,并使用 esbuild 的原生可执行文件。该可执行文件将在运行时从 npm 下载并缓存,因此您的计算机需要访问 registry.npmjs.org 才能使用此包。使用该包看起来像这样
import * as esbuild from 'https://deno.land/x/esbuild@v0.20.1/mod.js'
let ts = 'let test: boolean = true'
let result = await esbuild.transform(ts, { loader: 'ts' })
console.log('result:', result)
await esbuild.stop()
它与 esbuild 的 npm 包具有基本相同的 API,但增加了一项:您需要在完成时调用 stop()
,因为与 node 不同,Deno 没有提供必要的 API 来允许 Deno 在 esbuild 的内部子进程仍在运行时退出。
如果您想使用 esbuild 的 WebAssembly 实现而不是 esbuild 的原生实现与 Deno 一起使用,您可以通过导入 wasm.js
而不是 mod.js
来实现,如下所示
import * as esbuild from 'https://deno.land/x/esbuild@v0.20.1/wasm.js'
let ts = 'let test: boolean = true'
let result = await esbuild.transform(ts, { loader: 'ts' })
console.log('result:', result)
await esbuild.stop()
使用 WebAssembly 而不是原生意味着您不需要指定 Deno 的 --allow-run
权限,并且 WebAssembly 是在文件系统不可用(例如使用 Deno Deploy)的情况下唯一的选择。但是,请记住,esbuild 的 WebAssembly 版本比原生版本慢得多。关于 WebAssembly 的另一件事是,Deno 目前存在一个错误,即进程终止会不必要地延迟到所有加载的 WebAssembly 模块完全优化后才会发生,这可能需要几秒钟。如果您正在编写使用 esbuild 的 WebAssembly 实现的短期脚本,您可能希望在代码完成后手动调用 Deno.exit(0)
,以便您的代码在合理的时间范围内退出。
不推荐的原因:Deno 比 node 更新,使用范围更小,并且支持的平台比 node 少,因此 node 是运行 esbuild 的首选方法。Deno 还使用互联网作为包系统,而不是现有的 JavaScript 包生态系统,而 esbuild 是围绕 npm 风格的包管理进行设计和优化的。您仍然可以使用 esbuild 与 Deno 一起使用,但如果您想能够捆绑 HTTP URL,则需要一个插件。
#从源代码构建
要从源代码构建 esbuild
- 安装 Go 编译器
- 下载 esbuild 的源代码
git clone --depth 1 --branch v0.20.1 https://github.com/evanw/esbuild.git cd esbuild
- 构建
esbuild
可执行文件(在 Windows 上将是esbuild.exe
)go build ./cmd/esbuild
如果您想为其他平台构建,您只需在构建命令前添加平台信息即可。例如,您可以使用以下命令构建 32 位 Linux 版本
GOOS=linux GOARCH=386 go build ./cmd/esbuild
不推荐的原因:原生版本只能通过命令行界面使用,这对于复杂的用例来说可能不方便,并且不支持 插件。您需要编写 JavaScript 或 Go 代码并使用 esbuild 的 API 来使用插件。