Bun的快速原生捆綁器現(xiàn)在處于測(cè)試階段。它可以通過(guò) bun build CLI 命令或新的 Bun.build() JAVAScript API 使用。

從頭開(kāi)始捆綁 10 份三份.js副本,帶有源映射和縮小
使用捆綁器通過(guò)內(nèi)置 Bun.build() 函數(shù)或 bun build CLI 命令構(gòu)建前端應(yīng)用。
JavaScript
CLI
Bun.build({
entrypoints: ['./src/index.tsx'],
outdir: './build',
minify: true,
// additional config
});
降低 JavaScript 中的復(fù)雜性
JavaScript 最初是表單字段的自動(dòng)填充,如今它為將火箭發(fā)射到太空的儀器提供動(dòng)力。
不出所料,JavaScript 生態(tài)系統(tǒng)的復(fù)雜性呈爆炸式增長(zhǎng)。你如何運(yùn)行打字稿文件?您如何構(gòu)建/捆綁用于生產(chǎn)的代碼?該軟件包是否適用于 ESM?如何加載僅限本地的配置?是否需要安裝對(duì)等依賴項(xiàng)?如何使源映射正常工作?
復(fù)雜性需要時(shí)間,通常花費(fèi)在將工具粘合在一起或等待事情完成上。安裝 npm 包需要很長(zhǎng)時(shí)間。運(yùn)行測(cè)試應(yīng)該需要幾秒鐘(或更短)。為什么在 2023 年部署軟件需要幾分鐘,而在 2003 年將文件上傳到 FTP 服務(wù)器需要幾毫秒?
多年來(lái),我一直對(duì)JavaScript周圍的一切緩慢感到沮喪。當(dāng)從保存文件到測(cè)試更改的迭代周期時(shí)間變得足夠長(zhǎng),以至于本能地檢查黑客新聞時(shí),就出了問(wèn)題。
這種復(fù)雜性是有充分理由的。捆綁器和最小化器使網(wǎng)站加載速度更快。TypeScript 的編輯器內(nèi)交互式文檔使開(kāi)發(fā)人員的工作效率更高。類型安全有助于在將錯(cuò)誤交付給用戶之前捕獲錯(cuò)誤。作為版本控制包的依賴項(xiàng)通常比復(fù)制文件更易于維護(hù)。
當(dāng)“一件事”被拆分為如此多的孤立工具時(shí),“做好一件事”的Unix哲學(xué)就崩潰了。
這就是我們構(gòu)建 Bun 的原因,也是為什么今天我們很高興推出 Bun 捆綁器。
Yes, a new bundler是的,一個(gè)新的捆綁器
使用新的捆綁器,捆綁現(xiàn)在是 Bun 生態(tài)系統(tǒng)的一流元素,包括 bun build CLI 命令、新的頂級(jí) Bun.build 函數(shù)和穩(wěn)定的插件系統(tǒng)。
我們決定 Bun 需要自己的捆綁器有幾個(gè)原因。
Cohesiveness
捆綁器是一種元工具,它編排并支持所有其他工具,如 JSX、TypeScript、css 模塊和服務(wù)器組件——所有這些都需要捆綁器集成才能工作。
今天,捆綁器是JavaScript生態(tài)系統(tǒng)中巨大復(fù)雜性的來(lái)源。通過(guò)將捆綁引入JavaScript運(yùn)行時(shí),我們認(rèn)為我們可以使交付前端和全棧代碼更簡(jiǎn)單,更快。
- 快速插件。插件在輕量級(jí)的 Bun 進(jìn)程中執(zhí)行,啟動(dòng)速度快。
- 無(wú)冗余轉(zhuǎn)譯。使用 target: "bun" ,捆綁器生成針對(duì) Bun 運(yùn)行時(shí)優(yōu)化的預(yù)轉(zhuǎn)譯文件,從而提高運(yùn)行性能并避免不必要的重新轉(zhuǎn)譯。
- 統(tǒng)一的插件 API。Bun 提供了一個(gè)統(tǒng)一的插件 API,可以同時(shí)與捆綁器和運(yùn)行時(shí)一起使用。任何擴(kuò)展 Bun 捆綁能力的插件也可以用來(lái)擴(kuò)展 Bun 的運(yùn)行時(shí)能力。
- 運(yùn)行時(shí)集成。構(gòu)建返回一個(gè) BuildArtifact 對(duì)象的數(shù)組,這些對(duì)象實(shí)現(xiàn) Blob 接口,可以直接傳遞到 HTTP API 中,如 new Response() 。運(yùn)行時(shí)為 BuildArtifact 實(shí)現(xiàn)特殊的漂亮打印。
- 獨(dú)立可執(zhí)行文件。捆綁器可以通過(guò) --compile 標(biāo)志從 TypeScript 和 JavaScript 腳本生成獨(dú)立的可執(zhí)行文件。這些可執(zhí)行文件是完全獨(dú)立的,包括 Bun 運(yùn)行時(shí)的副本。
很快,捆綁器將與Bun的HTTP服務(wù)器API( Bun.serve )集成,從而可以用簡(jiǎn)單的聲明式API表示當(dāng)前復(fù)雜的構(gòu)建管道。稍后會(huì)詳細(xì)介紹。
Performance
這個(gè)不會(huì)讓任何人感到驚訝。作為一個(gè)運(yùn)行時(shí),Bun的代碼庫(kù)已經(jīng)包含了快速解析和轉(zhuǎn)換源代碼的基礎(chǔ)(用Zig實(shí)現(xiàn))。雖然可能,但很難與現(xiàn)有的本機(jī)捆綁器集成,并且進(jìn)程間通信所涉及的開(kāi)銷會(huì)損害性能。
最終,結(jié)果不言自明。在我們的基準(zhǔn)測(cè)試(源自 esbuild 的三.js基準(zhǔn)測(cè)試)中,Bun 比 esbuild 快 1.75 倍,比 Parcel 2 快 150 倍,比 Rollup + Terserr 快 180 倍,比 Webpack 快 220 倍。
Developer experience 開(kāi)發(fā)人員體驗(yàn)
查看現(xiàn)有捆綁器的 API,我們看到了很多改進(jìn)的空間。沒(méi)有人喜歡與捆綁器配置搏斗。Bun 的捆綁器 API 被設(shè)計(jì)為明確且不足為奇。說(shuō)到這里...
The API
該 API 目前在設(shè)計(jì)上是最小的。我們?cè)诖顺跏及姹局械哪繕?biāo)是實(shí)現(xiàn)一個(gè)最小功能集,該功能集快速、穩(wěn)定,并適應(yīng)大多數(shù)現(xiàn)代用例,而不會(huì)犧牲性能。
以下是當(dāng)前存在的 API:
interface Bun {
build(options: BuildOptions): Promise<BuildOutput>;
}
interface BuildOptions {
entrypoints: string[]; // required
outdir?: string; // default: no write (in-memory only)
target?: "browser" | "bun" | "node"; // "browser"
format?: "esm"; // later: "cjs" | "iife"
splitting?: boolean; // default false
plugins?: BunPlugin[]; // [] // see https://bun.sh/docs/bundler/plugins
loader?: { [k in string]: string }; // see https://bun.sh/docs/bundler/loaders
external?: string[]; // default []
sourcemap?: "none" | "inline" | "external"; // default "none"
root?: string; // default: computed from entrypoints
publicPath?: string; // e.g. http://mydomain.com/
naming?:
| string // equivalent to naming.entry
| { entry?: string; chunk?: string; asset?: string };
minify?:
| boolean // default false
| { identifiers?: boolean; whitespace?: boolean; syntax?: boolean };
}
其他捆綁器在追求功能完整性時(shí)做出了糟糕的架構(gòu)決策,最終導(dǎo)致性能下降;這是我們小心翼翼地試圖避免的錯(cuò)誤。
Module systems
目前僅支持 format: "esm" 。我們計(jì)劃添加對(duì)其他模塊系統(tǒng)和目標(biāo)的支持,如 iife 。如果有足夠多的人問(wèn),我們也會(huì)添加 cjs otuput 支持(支持 CommonJS 輸入,但不支持輸出)。
Targets
支持三個(gè)“目標(biāo)”: "browser" (默認(rèn)值)、 "bun" 和 "node" 。
browser
- TypeScript 和 JSX 會(huì)自動(dòng)轉(zhuǎn)換為原版 JavaScript。
- 模塊在可用時(shí)使用 "browser" package.json "exports" 條件解析
- 當(dāng)在瀏覽器中導(dǎo)入某些 Node.js API 時(shí),Bun 會(huì)自動(dòng)填充某些 Node API,例如 node:crypto ,類似于 Webpack 4 的行為。Bun 自己的 API 目前被禁止導(dǎo)入,但我們將來(lái)可能會(huì)重新審視這一點(diǎn)。
bun
- Bun 和 Node.js API 受支持且保持不變。
- 模塊使用 Bun 運(yùn)行時(shí)使用的默認(rèn)解析算法進(jìn)行解析。
- 生成的捆綁包用特殊的 // @bun 雜注注釋標(biāo)記,以指示它們是由 Bun 生成的。這向 Ban 的運(yùn)行時(shí)表明,在執(zhí)行之前不需要重新轉(zhuǎn)譯文件。協(xié)同!
node
目前,這與 target: "bun" 相同。將來(lái),我們計(jì)劃自動(dòng)填充 Bun API,例如 Bun 全局模塊和 bun:* 內(nèi)置模塊。
File types
捆綁器支持以下文件類型:
- .js .jsx .ts .tsx - JavaScript 和 TypeScript 文件。咄。
- .txt — 純文本文件。這些作為字符串內(nèi)聯(lián)。
- .json .toml — 這些在編譯時(shí)解析并內(nèi)聯(lián)為 JSON。
其他一切都被視為資產(chǎn)。資產(chǎn)按原樣復(fù)制到 outdir 中,導(dǎo)入將替換為文件的相對(duì)路徑或 URL,例如 /images/logo.png .
Input
Output
import logo from "./images/logo.png";
console.log(logo);
Plugins
與運(yùn)行時(shí)本身一樣,捆綁器被設(shè)計(jì)為可通過(guò)插件進(jìn)行擴(kuò)展。事實(shí)上,運(yùn)行時(shí)插件和捆綁器插件之間根本沒(méi)有區(qū)別。
import YamlPlugin from "bun-plugin-yaml";
const plugin = YamlPlugin();
// register a runtime plugin
Bun.plugin(plugin);
// register a bundler plugin
Bun.build({
entrypoints: ["./src/index.ts"],
plugins: [plugin],
});
Build outputs
Bun.build 函數(shù)返回一個(gè) Promise<BuildOutput> ,定義為:
interface BuildOutput {
outputs: BuildArtifact[];
success: boolean;
logs: Array<object>; // see docs for details
}
interface BuildArtifact extends Blob {
kind: "entry-point" | "chunk" | "asset" | "sourcemap";
path: string;
loader: Loader;
hash: string | null;
sourcemap: BuildArtifact | null;
}
outputs 數(shù)組包含生成生成的所有文件。每個(gè)項(xiàng)目都實(shí)現(xiàn) Blob 接口。
const build = await Bun.build({
/* */
});
for (const output of build.outputs) {
output.size; // file size in bytes
output.type; // MIME type of file
await output.arrayBuffer(); // => ArrayBuffer
await output.text(); // string
}
項(xiàng)目還包含以下附加屬性:
kind |
此文件是哪種類型的生成輸出。構(gòu)建會(huì)生成捆綁的入口點(diǎn)、代碼拆分的“塊”、源映射和復(fù)制的資產(chǎn)(如圖像)。 |
path |
磁盤上文件的絕對(duì)路徑或輸出路徑(如果文件未寫入磁盤)。 |
loader |
加載程序用于解釋文件。請(qǐng)參閱 捆綁程序 > 加載程序 以了解 Bun 如何將文件擴(kuò)展名映射到相應(yīng)的內(nèi)置加載程序。 |
hash |
文件內(nèi)容的哈希。始終為資產(chǎn)定義。 |
sourcemap |
與此文件對(duì)應(yīng)的源映射的另一個(gè) BuildArtifact (如果生成)。僅為入口點(diǎn)和區(qū)塊定義。 |
與 BunFile 類似, BuildArtifact 對(duì)象可以直接傳遞到 new Response() 中。
const build = Bun.build({
/* */
});
const artifact = build.outputs[0];
// Content-Type is set automatically
return new Response(artifact);
Bun 運(yùn)行時(shí)在記錄 BuildArtifact 對(duì)象時(shí)實(shí)現(xiàn)了特殊的漂亮打印,以便更輕松地進(jìn)行調(diào)試。
Build script
Shell output
// build.ts
const build = Bun.build({/* */});
const artifact = build.outputs[0];
console.log(artifact);
Server components
Bun 的捆綁器通過(guò) --server-components 標(biāo)志對(duì) React Server Components 提供了實(shí)驗(yàn)性支持。我們將在本周晚些時(shí)候發(fā)布其他文檔和示例項(xiàng)目。
Tree shaking
Bun 的捆綁器支持對(duì)未使用的代碼進(jìn)行樹搖晃。捆綁時(shí)始終啟用此功能。
package.json"sideEffects"field package.json"sideEffects"field
Bun 在 package.json 中支持 "sideEffects": false 。這是對(duì)捆綁器的提示,即該包沒(méi)有副作用,并且可以更積極地消除死代碼。
PURE__評(píng)論
Bun 支持 __PURE__ 注釋:
file.js
function foo() {
return 123;
}
/** #__PURE__ */ foo();
由于 foo 沒(méi)有副作用,這會(huì)導(dǎo)致一個(gè)空文件:
output.js
在 Webpack 的文檔 中了解更多信息。
process.env.NODE_ENVand--define
Bun 支持 NODE_ENV 環(huán)境變量和 --define CLI 標(biāo)志。這些通常用于有條件地在生產(chǎn)版本中包含代碼。
如果 process.env.NODE_ENV 設(shè)置為 "production" ,Bun 將自動(dòng)刪除包裝在 if (process.env.NODE_ENV !== "production") { ... } 中的代碼。
node-env.js
if (process.env.NODE_ENV !== "production") {
module.exports = require("./cjs/react.development.js");
} else {
module.exports = require("./cjs/react.production.min.js");
}
ES Module tree-shaking ES 模塊搖樹
ESM 樹搖動(dòng)適用于 ESM 和 CommonJS 輸入文件。Bun 的捆綁器會(huì)在安全的情況下自動(dòng)從 ESM 文件中刪除未使用的導(dǎo)出。
entry.js
foo.js
import { foo } from "./foo.js";
console.log(foo);
未使用的 bar 導(dǎo)出將被消除,從而導(dǎo)致:
output.js
// foo.js
var $foo = 456;
console.log($foo);
CommonJS tree-shaking
在有限的情況下,Bun 的捆綁器會(huì)自動(dòng)將 CommonJS 轉(zhuǎn)換為 ESM,運(yùn)行時(shí)開(kāi)銷為零。考慮這個(gè)微不足道的例子:
index.ts
foo.js
import { foo } from "./foo.js";
console.log(foo);
Bun 會(huì)自動(dòng)將 foo.js 轉(zhuǎn)換為 ESM,并對(duì)未使用的 exports 對(duì)象進(jìn)行樹搖晃。
Bundled
// foo.js
var $foo = 123;
// entry.js
console.log($foo);
請(qǐng)注意,在許多情況下,CommonJS的動(dòng)態(tài)特性使這變得非常困難。例如,考慮以下三個(gè)文件:
- entry.js
- foo.js
- bar.js
// entry.js
export default require("./foo");
Bun 無(wú)法在不執(zhí)行 foo.js 的情況下靜態(tài)確定它的導(dǎo)出。( Object.assign 也可以被覆蓋,這使得靜態(tài)分析在一般情況下是不可能的。在這種情況下,Bun 不會(huì)搖晃 exports 對(duì)象;相反,它會(huì)注入一些 CommonJS 運(yùn)行時(shí)代碼以使其按預(yù)期工作。
Source maps
捆綁器支持內(nèi)聯(lián)和外部源映射。
const build = await Bun.build({
entrypoints: ["./src/index.ts"],
// generates a *.js.map file alongside each output
sourcemap: "external",
// adds a base64-encoded `sourceMAppingURL` to the end of each output file
sourcemap: "inline",
});
console.log(await build.outputs[0].sourcemap.json()); // => { version: 3, ... }
Minifier
沒(méi)有縮小器的JavaScript捆綁器是不完整的。此版本還引入了內(nèi)置于 Bung 中的全新 JavaScript 縮減器。使用 minify: true 啟用縮小,或使用以下選項(xiàng)更精細(xì)地配置縮小行為:
{
minify?: boolean | {
identifiers?: boolean; // default: false
whitespace?: boolean; // default: false
syntax?: boolean; // default: false
}
}
縮小器能夠刪除死代碼,重命名標(biāo)識(shí)符,刪除空格,并智能地壓縮和內(nèi)聯(lián)常量值。
Input
Minified
// This comment will be removed!
console.log("this" + " " + "text" + " will" + " be " + "merged");
Jump in
我們更新了以下 React bun create 模板,以便在后臺(tái)使用 Bun.build 。運(yùn)行以下命令來(lái)搭建由 Bun 捆綁器提供支持的簡(jiǎn)單 React 項(xiàng)目的基架。
# a React single-page app
bun create react ./myapp
# a Next.js-like app with a /pages directory
# with SSR and client-side hydration
bun create react-ssr ./myapp
Sneak peek:Bun.App
捆綁器只是為更雄心勃勃的努力奠定了基礎(chǔ)。在接下來(lái)的幾個(gè)月里,我們將宣布 Bun.App :一個(gè)“超級(jí) API”,它將 Bung 的原生速度捆綁器、HTTP 服務(wù)器和文件系統(tǒng)路由器拼接成一個(gè)有凝聚力的整體。
目標(biāo)是讓使用Bun輕松表達(dá)任何類型的應(yīng)用程序,只需幾行代碼:
Static file server
API server
Next.js-style framework
new Bun.App({
bundlers: [
{
name: "static-server",
outdir: "./out",
},
],
routers: [
{
mode: "static",
dir: "./public",
build: "static-server",
},
],
});
app.serve();
app.build();
此 API 仍在積極討論中,可能會(huì)發(fā)生變化。