執行環境套件管理器打包器測試執行器程式碼檢查格式化指南參考部落格意見回饋

獨立 HTML

將單頁應用打包成一個無外部相依的自包含 .html 檔案

Bun 可以將整個前端打包成一個 .html 檔案,且不相依任何外部資源。JavaScript、TypeScript、JSX、CSS、圖片、字型、影片、WASM:Bun 會將所有內容內聯到一個檔案中。

terminal
bun build --compile --target=browser ./index.html --outdir=dist

輸出的是一個自包含的 HTML 文件:沒有相對路徑、沒有外部檔案,也不需要伺服器。

單檔案。隨處上傳。

輸出是一個單獨的 .html 檔案,你可以放到任何地方:

  • 上傳到 S3 或任何靜態檔案託管服務——無需維護目錄結構,只需一個檔案
  • 從桌面雙擊開啟——它會在瀏覽器中開啟並離線執行,無需本地主機伺服器
  • 嵌入到 WebView 中——無需處理相對檔案
  • 插入 <iframe> 中——只需一個檔案 URL,即可將互動式內容嵌入另一個頁面
  • 從任意位置提供服務——任何 HTTP 伺服器、CDN 或檔案共享服務都可以

無需安裝任何東西,無需部署 node_modules,無需協調建置產物,也無需考慮相對路徑。

真正的單檔案

通常,分發網頁意味著需要管理一個資原始檔夾——HTML、JavaScript 包、CSS 檔案和圖片。單獨移動 HTML 檔案而不帶上其他檔案,一切都會出錯。瀏覽器以前也嘗試過解決這個問題。Safari 的 .webarchive 和 Chrome 的 .mhtml 可以將網頁儲存為單個檔案。實際上,只有部分瀏覽器能開啟它們,這就失去了這樣做的意義。

獨立 HTML 輸出是一個普通的 .html 檔案:不是歸檔檔案,也不是資料夾。Bun 使用標準的 <style> 標籤、<script> 標籤和 data: URI,將每張圖片、每種字型以及每一行 CSS 和 JavaScript 都直接嵌入 HTML 中。任何瀏覽器都能開啟它,任何伺服器都能託管它。

你可以像分發 PDF 一樣分發頁面:這是一個可以移動、複製、上傳或分享的單一檔案,無需擔心路徑失效或資源缺失。

快速開始

index.html
<!doctype html>
<html>
  <head>
    <link rel="stylesheet" href="./styles.css" />
  </head>
  <body>
    <div id="root"></div>
    <script src="./app.tsx"></script>
  </body>
</html>
app.tsx
import React from "react";
import { createRoot } from "react-dom/client";

function App() {
  return <h1>來自單個 HTML 檔案的問候!</h1>;
}

createRoot(document.getElementById("root")!).render(<App />);
styles.css
body {
  margin: 0;
  font-family: system-ui, sans-serif;
  background: #f5f5f5;
}
terminal
bun build --compile --target=browser ./index.html --outdir=dist

開啟 dist/index.html —— React 應用無需伺服器即可執行。

所有內容均內聯

Bun 會將 HTML 中找到的每個本地資源都進行內聯:任何具有相對路徑的檔案,無論檔案型別是什麼,都會嵌入到輸出檔案中。

什麼內容會被內聯

在原始檔中在輸出檔案中
<script src="./app.tsx"><script type="module">...bundled code...</script>
<link rel="stylesheet" href="./styles.css"><style>...bundled CSS...</style>
<img src="./logo.png"><img src="data:image/png;base64,...">
<img src="./icon.svg"><img src="data:image/svg+xml;base64,...">
<video src="./demo.mp4"><video src="data:video/mp4;base64,...">
<audio src="./click.wav"><audio src="data:audio/x-wav;base64,...">
<source src="./clip.webm"><source src="data:video/webm;base64,...">
<video poster="./thumb.jpg"><video poster="data:image/jpeg;base64,...">
<link rel="icon" href="./favicon.ico"><link rel="icon" href="data:image/x-icon;base64,...">
<link rel="manifest" href="./app.webmanifest"><link rel="manifest" href="data:application/manifest+json;base64,...">
CSS 中的 url("./bg.png")CSS 中的 url(data:image/png;base64,...)
CSS 中的 @import "./reset.css"合併到 <style> 標籤中
CSS 中的 url("./font.woff2")CSS 中的 url(data:font/woff2;base64,...)
JS 中的 import "./styles.css"合併到 <style> 標籤中

圖片、字型、WASM 二進位制檔案、影片、音訊檔案、SVG:Bun 會將相對路徑引用的任何檔案編碼為 base64 格式的 data: URI,並直接嵌入 HTML 中。Bun 會根據副檔名檢測 MIME 型別。

Bun 會原樣保留外部 URL(例如 CDN 連結或絕對 URL)。

與 React 一起使用

React 應用無需額外設定:Bun 會轉譯 JSX 並解析 npm 包。

terminal
bun install react react-dom
index.html
<!doctype html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>我的應用</title>
    <link rel="stylesheet" href="./styles.css" />
  </head>
  <body>
    <div id="root"></div>
    <script src="./app.tsx"></script>
  </body>
</html>
app.tsx
import React, { useState } from "react";
import { createRoot } from "react-dom/client";
import { Counter } from "./components/Counter.tsx";

function App() {
  return (
    <main>
      <h1>單檔案 React 應用</h1>
      <Counter />
    </main>
  );
}

createRoot(document.getElementById("root")!).render(<App />);
components/Counter.tsx
import React, { useState } from "react";

export function Counter() {
  const [count, setCount] = useState(0);
  return <button onClick={() => setCount(count + 1)}>計數:{count}</button>;
}
terminal
bun build --compile --target=browser ./index.html --outdir=dist

Bun 會將所有 React 程式碼、元件和 CSS 打包到 dist/index.html 中。將這一個檔案上傳到任何地方,它都能正常執行。

與 Tailwind CSS 一起使用

安裝外掛並在 HTML 或 CSS 中引用 Tailwind:

terminal
bun install --dev bun-plugin-tailwind
index.html
<!doctype html>
<html>
  <head>
    <link rel="stylesheet" href="tailwindcss" />
  </head>
  <body class="bg-gray-100 flex items-center justify-center min-h-screen">
    <div id="root"></div>
    <script src="./app.tsx"></script>
  </body>
</html>
app.tsx
import React from "react";
import { createRoot } from "react-dom/client";

function App() {
  return (
    <div className="bg-white rounded-lg shadow-lg p-8 max-w-md">
      <h1 className="text-2xl font-bold text-gray-800">你好 Tailwind</h1>
      <p className="text-gray-600 mt-2">這是一個單檔案 HTML。</p>
    </div>
  );
}

createRoot(document.getElementById("root")!).render(<App />);

使用 JavaScript API 通過外掛建置:

build.ts
await Bun.build({
  entrypoints: ["./index.html"],
  compile: true,
  target: "browser",
  outdir: "./dist",
  plugins: [require("bun-plugin-tailwind")],
});
terminal
bun run build.ts

Bun 會將生成的 Tailwind CSS 以內聯 <style> 標籤的形式直接放入 HTML 檔案中。

工作原理

當你傳入 --compile --target=browser 且入口是 HTML 檔案時,Bun 會:

  1. 解析 HTML,發現所有 <script>、<link>、<img>、<video>、<audio>、<source> 及其他資源引用
  2. 將所有 JavaScript/TypeScript/JSX 打包為單個模組
  3. 將所有 CSS(包括 @import 鏈和 JS 中匯入的 CSS)打包成一個樣式表
  4. 將所有相對資源引用轉換為 base64 的 data: URI
  5. 將打包後的 JS 以內聯 <script type="module"> 形式放在 </body> 前
  6. 將打包後的 CSS 以內聯 <style> 形式放在 <head> 內
  7. 輸出一個不相依任何外部檔案的單個 .html 檔案。

壓縮

新增 --minify 來壓縮 JavaScript 和 CSS:

terminal
bun build --compile --target=browser --minify ./index.html --outdir=dist

或者使用 JavaScript API:

build.ts
await Bun.build({
  entrypoints: ["./index.html"],
  compile: true,
  target: "browser",
  outdir: "./dist",
  minify: true,
});

JavaScript API

使用 Bun.build() 以程式設計方式生成獨立的 HTML:

build.ts
const result = await Bun.build({
  entrypoints: ["./index.html"],
  compile: true,
  target: "browser",
  outdir: "./dist", // 可選,省略則作為 BuildArtifact 輸出
  minify: true,
});

if (!result.success) {
  console.error("建置失敗:");
  for (const log of result.logs) {
    console.error(log);
  }
} else {
  console.log("建置成功:", result.outputs[0].path);
}

省略 outdir 時,輸出會以 BuildArtifact 的形式儲存在 result.outputs 中:

TypeScript
const result = await Bun.build({
  entrypoints: ["./index.html"],
  compile: true,
  target: "browser",
});

const html = await result.outputs[0].text();
await Bun.write("output.html", html);

多個 HTML 檔案

你可以傳入多個 HTML 檔案作為入口,分別生成獨立 HTML 檔案:

terminal
bun build --compile --target=browser ./index.html ./about.html --outdir=dist

環境變數

使用 --env 將環境變數內聯到打包的 JavaScript 中:

terminal
API_URL=https://api.example.com bun build --compile --target=browser --env=inline ./index.html --outdir=dist

Bun 會在建置時將 JavaScript 中對 process.env.API_URL 的引用替換為字面值。

限制

  • 不支援程式碼拆分——--splitting 不能與 --compile --target=browser 一起使用
  • 大型資源會增加檔案大小,因為它們會被編碼為 base64(與原始二進位制檔案相比,體積增加 33%)
  • 外部 URL(CDN 連結、絕對 URL)會保持原樣:Bun 只內聯相對路徑