运行时包管理器打包器测试运行器代码检查格式化指南参考博客反馈

独立 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 只内联相对路径