Skip to content

文件类型

Bun 内置文件加载器详解

Bun 打包器内置了一系列默认加载器,让我们可以直接处理多种文件类型。通常来说,Bun 的打包器和运行时都支持相同的文件类型集合。

支持的文件类型

Bun 原生支持以下文件类型:

  • .js .cjs .mjs - JavaScript 文件
  • .mts .cts .ts .tsx .jsx - TypeScript 和 JSX 文件
  • .toml .json - 配置和数据文件
  • .txt .wasm .node .html - 其他常用文件类型

加载器工作原理

Bun 通过文件扩展名来确定应该使用哪个内置加载器来解析文件。每个加载器都有一个名称,如jstsxjson。这些名称在构建插件时用于扩展 Bun 的自定义加载器功能。

您可以使用loader导入属性显式指定要使用的加载器:

ts
// 使用toml加载器处理my_file文件
import my_toml from "./my_file" with { loader: "toml" };

内置加载器详解

让我们深入了解 Bun 提供的各种加载器及其特性。

JavaScript 加载器

js 加载器

适用于.cjs.mjs 文件。

这个加载器会解析 JavaScript 代码并应用一系列默认转换,如:

  • 消除死代码(Dead-code elimination)
  • 树摇(Tree shaking)优化

目前 Bun 不会尝试降级语法(即不会转换新语法为兼容旧浏览器的代码)

jsx 加载器

适用于.js.jsx 文件。

js加载器功能相同,但支持 JSX 语法。默认情况下,JSX 会被转换为纯 JavaScript。转换的具体方式取决于tsconfig.json中的jsx*编译器选项。

TIP

JSX 编译选项有关 JSX 编译选项的更多信息,可以参考TypeScript 的 JSX 文档

TypeScript 加载器

ts 加载器

适用于.ts.mts.cts 文件。

这个加载器会移除所有 TypeScript 语法,然后按照js加载器的方式处理代码。需要注意的是,Bun 不执行类型检查

tsx 加载器

适用于.tsx 文件。

同时转译 TypeScript 和 JSX 语法为纯 JavaScript。

数据文件加载器

json 加载器

适用于.json 文件。

JSON 文件可以直接导入到代码中,非常方便:

ts
import pkg from "./package.json";
console.log(pkg.name); // => "my-package"

在打包过程中,解析后的 JSON 会作为 JavaScript 对象内联到 bundle 中:

ts
var pkg = {
  name: "my-package",
  // ... 其他字段
};
console.log(pkg.name);

如果将.json文件作为打包器的入口点,它将被转换为一个.js模块,并通过export default导出解析后的对象。

json
{
  "name": "小明",
  "age": 35,
  "email": "[email protected]"
}
js
export default {
  name: "小明",
  age: 35,
  email: "[email protected]",
};

toml 加载器

适用于.toml 文件。

TOML 文件同样可以直接导入。Bun 使用其高性能的原生 TOML 解析器来处理这些文件。

ts
import config from "./bunfig.toml";
console.log(config.logLevel); // => "debug"

// 通过导入属性指定加载器:
// import myCustomTOML from './my.config' with {type: "toml"};

在打包过程中,解析后的 TOML 会作为 JavaScript 对象内联到 bundle 中,工作方式与 JSON 加载器类似:

ts
var config = {
  logLevel: "debug",
  // ...其他字段
};
console.log(config.logLevel);

如果将.toml文件作为入口点,它将被转换为一个.js模块,并通过export default导出解析后的对象。

toml
name = "小明"
age = 35
email = "[email protected]"
js
export default {
  name: "小明",
  age: 35,
  email: "[email protected]",
};

其他文件类型加载器

text 加载器

适用于.txt 文件。

文本文件可以直接导入。加载器会读取文件并将其内容作为字符串返回。

ts
import contents from "./file.txt";
console.log(contents); // => "你好,世界!"

// 将html文件作为文本导入
// 使用"type"属性可以覆盖默认加载器
import html from "./index.html" with { type: "text" };

在打包过程中,文件内容会被内联到 bundle 中作为字符串:

ts
var contents = `你好,世界!`;
console.log(contents);

如果将.txt文件作为入口点,它将被转换为一个.js模块,并通过export default导出文件内容。

txt
你好,世界!
js
export default "你好,世界!";

napi 加载器

适用于.node 文件。

在运行时,原生插件可以直接导入:

ts
import addon from "./addon.node";
console.log(addon);

在打包器中,.node文件使用file加载器处理。

sqlite 加载器

使用方式:通过with { "type": "sqlite" }导入属性指定。

在运行时和打包器中,SQLite 数据库可以直接导入。这会使用bun:sqliteAPI 加载数据库。

ts
import db from "./my.db" with { type: "sqlite" };

此功能仅在`target`为`bun`时支持。

默认情况下,数据库是与 bundle 分离的(外部引用),这样您可以使用在其他地方加载的数据库,因此磁盘上的数据库文件不会被打包到最终输出中。

您可以使用"embed"属性更改此行为:

ts
// 将数据库嵌入到bundle中
import db from "./my.db" with { type: "sqlite", embed: "true" };

当使用独立可执行文件时,数据库会被嵌入到单文件可执行文件中。

否则,要嵌入的数据库将被复制到outdir中,并使用哈希文件名。

html 加载器

HTML 加载器处理 HTML 文件并打包引用的资源。它会:

  • 打包并为引用的 JavaScript 文件生成哈希(<script src="...">
  • 打包并为引用的 CSS 文件生成哈希(<link rel="stylesheet" href="...">
  • 为引用的图片生成哈希(<img src="...">
  • 保留外部 URL(默认情况下,任何以http://https://开头的 URL)

例如,给定以下 HTML 文件:

html
<!DOCTYPE html>
<html>
  <body>
    <img src="./image.jpg" alt="本地图片" />
    <img src="https://example.com/image.jpg" alt="外部图片" />
    <script type="module" src="./script.js"></script>
  </body>
</html>

它将输出一个带有打包资源的新 HTML 文件:

html
<!DOCTYPE html>
<html>
  <body>
    <img src="./image-HASHED.jpg" alt="本地图片" />
    <img src="https://example.com/image.jpg" alt="外部图片" />
    <script type="module" src="./output-ALSO-HASHED.js"></script>
  </body>
</html>

在内部,它使用lol-html提取脚本和链接标签作为入口点,以及其他资源作为外部资源。

HTML 加载器支持处理的选择器列表包括:

  • audio[src] - 音频元素
  • iframe[src] - 内联框架
  • img[src] - 图片源
  • img[srcset] - 响应式图片源
  • link[rel='stylesheet'][href] - 样式表链接
  • link[rel='icon'][href] - 网站图标
  • script[src] - 脚本源
  • source[src] - 媒体源
  • video[src] - 视频源
  • 以及更多...

sh 加载器

适用于.sh 文件。

此加载器用于解析Bun Shell脚本。它仅在启动 Bun 本身时受支持,因此在打包器或运行时中不可用。

sh
$ bun run ./script.sh

file 加载器

适用于:所有未识别的文件类型。

文件加载器将导入解析为导入文件的路径/URL。它通常用于引用媒体或字体资源。

ts
import logo from "./logo.svg";
console.log(logo);

在运行时,Bun 检查logo.svg文件是否存在,并将其转换为磁盘上logo.svg位置的绝对路径。

bash
$ bun run logo.ts
/path/to/project/logo.svg

在打包器中,情况略有不同。文件会原样复制到outdir中,导入会被解析为指向复制文件的相对路径。

ts
var logo = "./logo.svg";
console.log(logo);

如果为publicPath指定了值,导入将使用该值作为前缀来构建绝对路径/URL。

公共路径解析后的导入
"" (默认)/logo.svg
"/assets"/assets/logo.svg
"https://cdn.example.com/"https://cdn.example.com/logo.svg

复制文件的位置和文件名由[`naming.asset`](https://bun.sh/docs/bundler#naming)的值确定。

文件加载器使用场景

下面是一个常见问题和解决方案示例,展示如何在实际项目中利用 Bun 的文件加载器:

问题:在 React 项目中如何正确导入各种资源?

在前端开发中,我们经常需要导入多种类型的文件,如配置文件、图片、样式表等。

解决方案:

创建一个典型的 React 项目结构,并正确导入各种资源:

my-app/
├── src/
│   ├── assets/
│   │   ├── logo.svg
│   │   └── background.png
│   ├── config/
│   │   ├── settings.json
│   │   └── app.toml
│   ├── components/
│   │   └── Header.tsx
│   └── App.tsx
└── package.json

App.tsx中导入不同类型的文件:

tsx
import React from 'react';
// 导入图片资源
import logo from './assets/logo.svg';
import background from './assets/background.png';

// 导入配置文件
import settings from './config/settings.json';
import appConfig from './config/app.toml' with { type: "toml" };

// 导入组件
import Header from './components/Header';

function App() {
  return (
    <div style={{ backgroundImage: `url(${background})` }}>
      <Header />
      <img src={logo} alt="Logo" width={100} />
      <h1>{settings.appName}</h1>
      <p>版本: {appConfig.version}</p>
      <p>环境: {appConfig.environment}</p>
    </div>
  );
}

export default App;

分析过程

  1. 图片资源:使用file加载器处理,返回 URL 路径
  2. JSON 配置:使用json加载器解析,返回 JavaScript 对象
  3. TOML 配置:使用toml加载器解析,返回 JavaScript 对象
  4. React 组件:使用tsx加载器处理 TypeScript 和 JSX 语法

在开发过程中,Bun 会自动选择正确的加载器处理不同类型的文件,使得导入体验非常流畅。在构建时,Bun 会正确打包这些资源,并优化最终的输出。

解决 TypeScript 导入错误

使用 TypeScript 时,当导入非 JavaScript/TypeScript 文件时,可能会遇到类似这样的错误:

TypeScript错误
Cannot find module './logo.svg' or its corresponding type declarations.

解决方法是创建一个类型声明文件(*.d.ts),在项目中的任何位置都可以(文件名不重要),内容如下:

ts
declare module "*.svg" {
  const content: string;
  export default content;
}

这告诉 TypeScript,任何来自.svg的默认导入应该被视为字符串类型。

最佳实践与建议

  1. 明确指定加载器:当文件扩展名与其内容不匹配时,使用with { loader: "xxx" }来明确指定加载器类型

  2. 为非标准资源添加 TypeScript 声明:为了获得更好的 TypeScript 支持,为常用的非代码资源(如图片、SVG、字体等)创建类型声明文件

  3. 利用 JSON 和 TOML 导入:将配置集中在 JSON 或 TOML 文件中,可以使代码更整洁、更易维护

  4. 了解资源处理方式:理解在运行时和打包时资源处理的区别,特别是对于媒体文件和外部资源

TIP

当处理大量资源时,考虑使用 Bun 的插件系统创建自定义加载器,以满足特定的项目需求。

总结

Bun 的内置文件加载器系统使处理各种文件类型变得简单高效。通过了解不同加载器的工作原理和最佳实践,您可以充分利用 Bun 的功能来构建高性能的应用程序。

最重要的是,这种无缝的文件处理方式允许开发者专注于应用逻辑,而不必担心文件类型兼容性和转换问题。