開始使用

安裝 esbuild

首先,下載並在本地安裝 esbuild 指令。可以使用 npm 安裝預先建置的原生可執行檔(當你安裝 node JavaScript 執行環境時會自動安裝)

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

這應該會在你的本地 node_modules 資料夾中安裝 esbuild。你可以執行 esbuild 可執行檔來驗證一切是否運作正常

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

建議使用 npm 安裝原生可執行檔來安裝 esbuild。但如果你不想這樣做,還有一些 其他安裝方式

你的第一個套件

以下是一個 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 編寫 JavaScript 建立腳本。它可能看起來像這樣(請注意,此程式碼必須儲存在副檔名為 .mjs 的檔案中,因為它使用 import 關鍵字)

import * as esbuild from 'esbuild'

await esbuild.build({
  entryPoints: ['app.jsx'],
  bundle: true,
  outfile: 'out.js',
})

build 函式會在子處理序中執行 esbuild 可執行檔,並傳回一個承諾,在建立完成時解析。還有一個非同步的 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 功能。

針對節點進行打包

即使在使用節點時不需要打包器,但在節點中執行程式碼之前,有時仍可以受益於使用 esbuild 處理您的程式碼。打包可以自動移除 TypeScript 型別,將 ECMAScript 模組語法轉換為 CommonJS,並將較新的 JavaScript 語法轉換為特定節點版本的較舊語法。在發佈套件之前,打包套件可能是有益的,這樣可以減少下載大小,並在載入時減少從檔案系統讀取的時間。

如果您要打包將在節點中執行的程式碼,則應透過將 --platform=node 傳遞給 esbuild 來組態 platform 設定。這會同時將一些不同的設定變更為對節點友善的預設值。例如,所有內建於節點的套件(例如 fs)都會自動標記為外部,因此 esbuild 不會嘗試將它們打包。此設定也會停用對 package.json 中 browser 欄位的詮釋。

如果您的程式碼使用較新的 JavaScript 語法,而您的節點版本不支援,您將需要組態節點的 target 版本

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 來打包您的相依性。有許多 esbuild 在打包時不支援的節點特定功能,例如 __dirnameimport.meta.urlfs.readFileSync*.node 原生二進位模組。您可以透過將 packages 設定為 external 來從套件中排除所有相依性

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

然而,人們有時會因為在 Windows 或 macOS 上安裝 esbuild,然後將其 node_modules 目錄複製到執行 Linux 的 Docker 映像中,或在 Windows 和 WSL 環境之間複製其 node_modules 目錄而陷入此情況。讓此情況正常運作的方法取決於您的套件管理員

如果您使用 ARM 版本的 npm 安裝 esbuild,但隨後嘗試使用在 Rosetta 中執行的 x86-64 版本的 node 來執行 esbuild,您也可能會在配備 ARM 處理器的 macOS 電腦上陷入此情況。在這種情況下,一個簡單的解決方法是改用 ARM 版本的 node 執行您的程式碼,您可以在此處下載:https://node.dev.org.tw/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(即不使用 yarn esbuild)的情況下執行 esbuild。這可能會讓 esbuild 執行速度快 10 倍。

其他安裝方式

安裝 esbuild 的建議方式是 使用 npm 安裝原生可執行檔。但您也可以使用以下方式安裝 esbuild

下載組建

如果您有 Unix 系統,可以使用以下命令下載適用於您當前平台的 esbuild 二進制可執行檔(它將下載到當前工作目錄)

curl -fsSL https://esbuild.dev.org.tw/dl/v0.20.1 | sh

您也可以使用 latest 而不是版本號碼來下載最新版本的 esbuild

curl -fsSL https://esbuild.dev.org.tw/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 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-x64 netbsd1 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 版本還排除了一些功能,例如本機檔案伺服器。只有在沒有其他選擇時,例如當您想在不受支援的平台上使用 esbuild 時,您才應該這樣使用 WebAssembly 套件。WebAssembly 套件主要僅供 在瀏覽器中 使用。

使用 Deno 取代 node

如果您想使用 esbuild 取代 Deno,那麼對於 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()

它的 API 與 esbuild 的 npm 套件基本上相同,但有一個新增功能:您需要在完成後呼叫 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 權限,且在檔案系統不可用(例如使用 Deno Deploy)的情況下,WebAssembly 是唯一選項。不過,請注意 esbuild 的 WebAssembly 版本比原生版本慢很多。關於 WebAssembly 的另一件事是,Deno 目前有一個錯誤,在所有載入的 WebAssembly 模組完全最佳化之前,處理程序終止會不必要地延遲,這可能需要數秒。如果您撰寫的是使用 esbuild 的 WebAssembly 實作的短暫腳本,您可能希望在程式碼執行完畢後手動呼叫 Deno.exit(0),讓您的程式碼在合理的時間內結束。

不建議這樣做的原因:Deno 比 node 新,使用較不廣泛,且支援的平台比 node 少,因此建議使用 node 作為執行 esbuild 的主要方式。Deno 也使用網際網路作為套件系統,而不是現有的 JavaScript 套件生態系統,而 esbuild 是針對 npm 式套件管理設計和最佳化的。您仍然可以使用 Deno 使用 esbuild,但如果您想要能夠綑綁 HTTP URL,您將需要外掛程式。

從原始碼建置

從原始碼建置 esbuild

  1. 安裝 Go 編譯器

    https://go.dev.org.tw/dl/

  2. 下載 esbuild 的原始碼
    git clone --depth 1 --branch v0.20.1 https://github.com/evanw/esbuild.git
    cd esbuild
    
  3. 建置 esbuild 可執行檔(在 Windows 上將會是 esbuild.exe
    go build ./cmd/esbuild

如果您想要建置其他平台,您只要在建置指令前加上平台資訊即可。例如,您可以使用這個指令建置 32 位元 Linux 版本

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

不建議這樣做的原因:原生版本只能透過命令列介面使用,這對於複雜的使用案例來說可能不符合人體工學,且不支援 外掛程式。您需要撰寫 JavaScript 或 Go 程式碼並使用 esbuild 的 API 才能使用外掛程式。