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