入门指南

安装esbuild

首先,在本地下载并安装esbuild命令。可以使用npm 安装预构建的原生可执行文件(在安装node JavaScript运行时时会自动安装npm):

npm install --save-exact --save-dev esbuild

这应该已经在本地的node_modules文件夹中安装了esbuild。 你可以运行esbuild可执行文件来验证一切是否正常工作:

Unix Windows
./node_modules/.bin/esbuild --version
.\node_modules\.bin\esbuild --version

安装esbuild的推荐方式是使用npm安装原生可执行文件。但如果你不想这样做, 还有一些其他安装方式

你的第一个打包

这是一个关于esbuild功能和使用方法的快速实例。首先,安装reactreact-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打包该文件:

Unix Windows
./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语法,可以使用--loader:.js=jsx标志告诉esbuild允许这样做。 你可以在API文档中阅读更多关于可用配置选项的信息。

构建脚本

你的构建命令是你将重复运行的东西,所以你会想要自动化它。 一个自然的方式是在你的package.json文件中添加一个构建脚本,如下所示:

{
  "scripts": {
    "build": "esbuild app.jsx --bundle --outfile=out.js"
  }
}

注意,这直接使用esbuild命令,而不是相对路径。这是可行的,因为scripts部分中的所有内容 都是在路径中已经包含esbuild命令的情况下运行的(只要你已经安装了该包)。

构建脚本可以这样调用:

npm run build

然而,如果你需要向esbuild传递许多选项,使用命令行界面可能会变得笨重。 对于更复杂的用途,你可能想使用esbuild的JavaScript API编写构建脚本。 它可能看起来像这样(注意,这段代码必须保存在扩展名为.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语法。所有这些可能看起来像这样:

CLI JS Go
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时不需要打包,有时将代码与esbuild一起处理也是有好处的。 打包可以自动剥离TypeScript类型,将ECMAScript模块语法转换为CommonJS, 并将新的JavaScript语法转换为旧语法,以便为特定版本的node处理。 它可能有助于在发布之前打包你的包,以便它是一个更小的下载, 并且它花费更少的时间从文件系统中读取。

如果你正在打包将要在node中运行的代码,你应该配置平台设置, 通过传递--platform=node给esbuild。 这同时改变了几个设置为node友好的默认值。例如,所有内置到node中的包,如fs, 都会自动标记为外部包,因此esbuild不会尝试打包它们。此设置还会禁用 package.json中的浏览器字段解释。

如果你的代码使用新的JavaScript语法,但无法在您的node版本中运行, 您将希望配置node的目标版本:

CLI JS Go
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在打包时无法支持的,如__dirnameimport.meta.urlfs.readFileSync, 以及*.node本机二进制模块。你可以通过设置packages 为外部来排除所有依赖:

CLI JS Go
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

但是,人们有时会进入这种情况,方法是安装esbuild 在Windows或macOS上,并将他们的node_modules目录复制到 Docker图像,该图像运行Linux,或者在Windows和WSL 环境之间复制他们的node_modules目录。获取此工作的方法取决于您的包管理器:

你也可以在macOS计算机上进入这种情况,如果你使用ARM版本的npm安装esbuild, 然后尝试使用x86-64版本的node运行esbuild。在这种情况下,一个简单的修复方法是 使用ARM版本的node运行你的代码,你可以在这里下载:https://nodejs.org/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倍。

其他安装方式

安装esbuild的推荐方式是使用npm安装原生可执行文件。 但你也可以以这些方式安装esbuild:

下载构建

如果你有一个Unix系统,你可以使用以下命令下载 当前平台的esbuild二进制可执行文件(它将下载到当前工作目录):

curl -fsSL https://esbuild.github.io/dl/v0.25.5 | sh

你也可以使用latest而不是版本号来下载 esbuild的最新版本:

curl -fsSL https://esbuild.github.io/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.25.5.tgz
tar xzf ./darwin-x64-0.25.5.tgz
./package/bin/esbuild
Usage:
  esbuild [options] [entry points]

...

@esbuild/darwin-x64包中的本机可执行文件是为macOS 操作系统和64位Intel架构设计的。截至撰写本文时,这是esbuild支持的平台本机可执行文件的完整列表:

Package name OS Architecture Download
@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 loong642
@esbuild/linux-mips64el linux mips64el2
@esbuild/linux-ppc64 linux ppc64
@esbuild/linux-riscv64 linux riscv642
@esbuild/linux-s390x linux s390x
@esbuild/linux-x64 linux x64
@esbuild/netbsd-arm64 netbsd1 arm64
@esbuild/netbsd-x64 netbsd1 x64
@esbuild/openbsd-arm64 openbsd arm64
@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系统,因此需要WSL 在Windows上。另一个缺点是您无法使用插件 与本机版本的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

还有对Deno JavaScript环境的初步支持,如果你想使用esbuild与它,而不是node。 包托管在https://deno.land/x/esbuild 并使用本机esbuild可执行文件。可执行文件将从npm下载并缓存到本地,因此您的计算机需要网络访问 registry.npmjs.org才能使用此包。使用此包看起来像这样:

import * as esbuild from 'https://deno.land/x/esbuild@v0.25.5/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.25.5/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。 但是,请记住,WebAssembly版本的esbuild比本机版本慢得多。 另一个关于WebAssembly的事情是,Deno目前有一个错误, 其中进程终止被不必要的延迟,直到所有加载的WebAssembly模块完全优化, 这可能需要很多秒。如果你正在编写一个短暂的脚本, 使用esbuild的WebAssembly实现,你可能想在完成后手动调用Deno.exit(0), 以便你的代码在合理的时间内退出。

为什么这不推荐: Deno比node新,使用范围小,支持的平台比node少,所以推荐node作为主要运行esbuild的方式。 Deno还使用互联网作为包系统,而不是现有的JavaScript包生态系统, esbuild是为npm风格的包管理而设计和优化的。你应该仍然能够使用esbuild与Deno, 但如果你希望能够捆绑HTTP URL,则需要一个插件。

从源代码构建

要构建esbuild的源代码:

  1. 安装Go编译器:

    https://go.dev/dl/

  2. 下载esbuild的源代码:
    git clone --depth 1 --branch v0.25.5 https://github.com/evanw/esbuild.git
    cd esbuild
    
  3. 构建esbuild可执行文件(它将是esbuild.exe在Windows上):
    go build ./cmd/esbuild

如果你想要为其他平台构建,你可以只在前缀构建命令中添加平台信息。例如,你可以使用此命令构建 32位Linux版本:

GOOS=linux GOARCH=386 go build ./cmd/esbuild

为什么这不推荐: 本机版本只能通过命令行界面使用,这对于复杂用例来说可能是不便的, 并且不支持插件。你需要编写JavaScript或Go代码并使用esbuild的API 使用插件。