> ## Documentation Index
> Fetch the complete documentation index at: https://bun.ll1025.cn/llms.txt
> Use this file to discover all available pages before exploring further.

# 独立 HTML

> 将单页应用打包到单个独立的 .html 文件中，无需任何外部依赖

Bun 可以将整个前端打包成一个**单一的 `.html` 文件**，零外部依赖。JavaScript、TypeScript、JSX、CSS、图片、字体、视频、WASM——所有内容都内联到一个文件中。

```bash terminal icon="terminal" theme={null}
bun build --compile --target=browser ./index.html --outdir=dist
```

输出是一个自包含的 HTML 文档：没有相对路径，没有外部文件，不需要服务器。

## 一个文件。随处上传。

输出是一个单一的 `.html` 文件，你可以放在任何地方：

* **上传到 S3** 或任何静态文件托管——无需维护目录结构，一个文件搞定
* **从桌面双击**——它在浏览器中打开并可离线工作，无需 localhost 服务器
* **嵌入到你的 webview**——无需处理相对文件
* **插入到 `<iframe>` 中**——使用单个文件 URL 在另一个页面中嵌入交互式内容
* **从任何地方提供**——任何 HTTP 服务器、CDN 或文件共享

无需安装任何东西，无需部署 `node_modules`，无需协调构建产物，无需考虑相对路径。

## 真正的一个文件

通常，分发一个网页意味着管理一个资源文件夹——HTML、JavaScript 包、CSS 文件、图片。只移动 HTML 而没有其他文件，一切都会崩溃。浏览器以前尝试过解决这个问题：Safari 的 `.webarchive` 和 `.mhtml` 本应将页面保存为单个文件，但在实践中它们会在你的计算机上解包成一个松散的文件夹——违背了目的。

独立 HTML 输出是一个纯 `.html` 文件：不是归档文件，不是文件夹。每张图片、每种字体、每行 CSS 和 JavaScript 都使用标准的 `<style>` 标签、`<script>` 标签和 `data:` URI 直接嵌入到 HTML 中。任何浏览器都可以打开它，任何服务器都可以托管它。

你可以像分发 PDF 一样分发该页面：一个可以移动、复制、上传或共享的文件，无需担心路径损坏或丢失资源。

## 快速开始

<CodeGroup>
  ```html index.html icon="file-code" theme={null}
  <!doctype html>
  <html>
    <head>
      <link rel="stylesheet" href="./styles.css" />
    </head>
    <body>
      <div id="root"></div>
      <script src="./app.tsx"></script>
    </body>
  </html>
  ```

  ```tsx app.tsx icon="https://mintcdn.com/span-inc/82N53aP7NFbaCVSl/icons/typescript.svg?fit=max&auto=format&n=82N53aP7NFbaCVSl&q=85&s=787d39d6a7d96d7f9540dc74344eba23" theme={null}
  import React from "react";
  import { createRoot } from "react-dom/client";

  function App() {
    return <h1>来自单个 HTML 文件的问候！</h1>;
  }

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

  ```css styles.css icon="file-code" theme={null}
  body {
    margin: 0;
    font-family: system-ui, sans-serif;
    background: #f5f5f5;
  }
  ```
</CodeGroup>

```bash terminal icon="terminal" theme={null}
bun build --compile --target=browser ./index.html --outdir=dist
```

打开 `dist/index.html`——React 应用无需服务器即可工作。

## 所有内容都被内联

Bun 会内联在 HTML 中找到的每个本地资源：任何具有相对路径的、任何文件类型的，都会被嵌入到输出文件中。

### 哪些内容会被内联

| 源代码中                                             | 输出中                                                                      |
| ------------------------------------------------ | ------------------------------------------------------------------------ |
| `<script src="./app.tsx">`                       | `<script type="module">...打包后的代码...</script>`                            |
| `<link rel="stylesheet" href="./styles.css">`    | `<style>...打包后的 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——任何由相对路径引用的文件都会被 base64 编码为 `data:` URI 并直接嵌入到 HTML 中。Bun 从文件扩展名检测 MIME 类型。

外部 URL（如 CDN 链接或绝对 URL）保持不变。

## 使用 React

React 应用无需额外配置：Bun 转译 JSX 并解析 npm 包。

```bash terminal icon="terminal" theme={null}
bun install react react-dom
```

<CodeGroup>
  ```html index.html icon="file-code" theme={null}
  <!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>
  ```

  ```tsx app.tsx icon="https://mintcdn.com/span-inc/82N53aP7NFbaCVSl/icons/typescript.svg?fit=max&auto=format&n=82N53aP7NFbaCVSl&q=85&s=787d39d6a7d96d7f9540dc74344eba23" theme={null}
  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 />);
  ```

  ```tsx components/Counter.tsx icon="https://mintcdn.com/span-inc/82N53aP7NFbaCVSl/icons/typescript.svg?fit=max&auto=format&n=82N53aP7NFbaCVSl&q=85&s=787d39d6a7d96d7f9540dc74344eba23" theme={null}
  import React, { useState } from "react";

  export function Counter() {
    const [count, setCount] = useState(0);
    return <button onClick={() => setCount(count + 1)}>计数：{count}</button>;
  }
  ```
</CodeGroup>

```bash terminal icon="terminal" theme={null}
bun build --compile --target=browser ./index.html --outdir=dist
```

整个 React、你的组件和你的 CSS 都被打包到 `dist/index.html` 中。将这一个文件上传到任何地方，它都能正常工作。

## 使用 Tailwind CSS

安装插件并在 HTML 或 CSS 中引用 Tailwind：

```bash terminal icon="terminal" theme={null}
bun install --dev bun-plugin-tailwind
```

<CodeGroup>
  ```html index.html icon="file-code" theme={null}
  <!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>
  ```

  ```tsx app.tsx icon="https://mintcdn.com/span-inc/82N53aP7NFbaCVSl/icons/typescript.svg?fit=max&auto=format&n=82N53aP7NFbaCVSl&q=85&s=787d39d6a7d96d7f9540dc74344eba23" theme={null}
  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 />);
  ```
</CodeGroup>

使用 JavaScript API 配合插件构建：

```ts build.ts icon="https://mintcdn.com/span-inc/82N53aP7NFbaCVSl/icons/typescript.svg?fit=max&auto=format&n=82N53aP7NFbaCVSl&q=85&s=787d39d6a7d96d7f9540dc74344eba23" theme={null}
await Bun.build({
  entrypoints: ["./index.html"],
  compile: true,
  target: "browser",
  outdir: "./dist",
  plugins: [require("bun-plugin-tailwind")],
});
```

```bash terminal icon="terminal" theme={null}
bun run build.ts
```

生成的 Tailwind CSS 直接以内嵌 `<style>` 标签的形式内联到 HTML 文件中。

## 原理

当你使用 `.html` 入口点传递 `--compile --target=browser` 时，Bun：

1. 解析 HTML 并发现所有 `<script>`、`<link>`、`<img>`、`<video>`、`<audio>`、`<source>` 和其他资源引用
2. 将所有 JavaScript/TypeScript/JSX 打包到一个模块中
3. 将所有 CSS（包括 `@import` 链和从 JS 导入的 CSS）打包到单个样式表中
4. 将每个相对资源引用转换为 base64 `data:` URI
5. 将打包后的 JS 内联为 `</body>` 之前的 `<script type="module">`
6. 将打包后的 CSS 内联为 `<head>` 中的 `<style>`
7. 输出一个无外部依赖的单一 `.html` 文件

## 压缩

添加 `--minify` 来压缩 JavaScript 和 CSS：

```bash terminal icon="terminal" theme={null}
bun build --compile --target=browser --minify ./index.html --outdir=dist
```

或使用 JavaScript API：

```ts build.ts icon="https://mintcdn.com/span-inc/82N53aP7NFbaCVSl/icons/typescript.svg?fit=max&auto=format&n=82N53aP7NFbaCVSl&q=85&s=787d39d6a7d96d7f9540dc74344eba23" theme={null}
await Bun.build({
  entrypoints: ["./index.html"],
  compile: true,
  target: "browser",
  outdir: "./dist",
  minify: true,
});
```

## JavaScript API

使用 `Bun.build()` 以编程方式生成独立 HTML：

```ts build.ts icon="https://mintcdn.com/span-inc/82N53aP7NFbaCVSl/icons/typescript.svg?fit=max&auto=format&n=82N53aP7NFbaCVSl&q=85&s=787d39d6a7d96d7f9540dc74344eba23" theme={null}
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` 中可用：

```ts icon="https://mintcdn.com/span-inc/82N53aP7NFbaCVSl/icons/typescript.svg?fit=max&auto=format&n=82N53aP7NFbaCVSl&q=85&s=787d39d6a7d96d7f9540dc74344eba23" theme={null}
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 文件：

```bash terminal icon="terminal" theme={null}
bun build --compile --target=browser ./index.html ./about.html --outdir=dist
```

## 环境变量

使用 `--env` 将环境变量内联到打包后的 JavaScript 中：

```bash terminal icon="terminal" theme={null}
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）保持不变——只有相对路径被内联
