你以为加载个文本文件只能靠 fetch?今天 Import Text 把这件事彻底原生化了

你想在 JS 里加载一个 .txt 文件内容,最常见的写法是什么?

const res = await fetch('./readme.txt');
const text = await res.text();

两行,但它是异步的。你得处理 loading 状态,得确保文件真的被 fetch 到了,得处理 CORS,在 SSR 场景下还得换方案。

而现在,这事一行同步语句就搞定了:

import text from './readme.txt' with { type: 'text' };
console.log(text); // 直接就是字符串

这就是 Import TextTC39 Stage 3 提案,Eemeli Aro 主导,Bun/Deno/Firefox Nightly 已落地。

它解决了什么问题

以前想在一个 JS 模块里直接引用一个文本文件,你绕不开这几条路:

方案一:fetch 异步

const res = await fetch('./template.html');
const html = await res.text();

永远是异步,代码里多一层 await,SSR 还要做特殊处理。

方案二:打包工具的 ?raw

import html from './template.html?raw';

这是 Vite/Rollup 的特殊语法,离开了 bundler 就跑不了,也没有类型提示。

方案三:Node fs 模块

import { readFileSync } from 'fs';
const content = readFileSync('./readme.txt', 'utf-8');

只能在 Node 端跑,浏览器完全不支持,路径计算还得配合 import.meta.url

Import Text 把这事做成了语言级别的标准语法,跟 import json from './data.json' with { type: 'json' } 完全平级:

// 加载 Markdown 配置文件
import configMd from './config.md' with { type: 'text' };

// 加载 SVG 图标作为字符串
import svgIcon from './arrow.svg' with { type: 'text' };

// 加载 HTML partial
import partial from './partial.html' with { type: 'text' };
document.querySelector('div').setHTML(partial, { sanitizer: {} });

现在的支持情况

运行时:

  • Bun:从 2024 年 4 月起原生支持,无需任何 flag
  • Deno:2.4+ 支持,需 --unstable-raw-imports flag
  • Firefox Nightly:153.0a1 已支持
  • Chrome/Safari:还在路上

Node.js:26.5.0 实验性支持,需 --experimental-import-text flag。

Bundler 层面:Vite 用户可以用 vite-import-attributes 插件将 with { type: 'text' } 语法转译成 Vite 自己的 ?raw 查询参数,在浏览器环境中提前落地支持。

也就是说:现在在 Bun 里可以直接跑,在 Deno 加个 flag 可以用,在 Vite 项目里装个插件全浏览器都支持。

和 JSON 模块是同一套设计

Import Text 不是拍脑袋想出来的新语法。它是 Import Attributes 体系的一部分,和你已经用得很熟的 JSON 模块是一套设计:

// JSON 模块 —— 2019 年落地
import data from './data.json' with { type: 'json' };

// 文本模块 —— Import Text,Stage 3
import text from './readme.txt' with { type: 'text' };

// 字节模块 —— Import Bytes,同步推进中
import bytes from './image.png' with { type: 'bytes' };

Import Attributes 体系的核心语义是:告诉运行时「这个被导入的东西不是 JS,按它的类型来处理」。文本文件导出一个字符串,JSON 文件导出一个对象,bytes 导出一个 Uint8Array,各是各的类型,编译器/bundler/运行时各司其职。

适合哪些场景

文档类站点:把 Markdown/SVG 作为静态资源 import 进来,不用 fetch,不用担心路径问题。

配置内联:把 YAML/TOML 配置文件 import 进来,在打包时就能拿到字符串,SSR 场景也能用。

代码展示:文档站里要展示源码,把源文件直接 import 进来渲染,不用写死字符串。

SVG 图标库:把 SVG 作为字符串导入,在运行时做 DOM 操作或者动态替换颜色,不用走 img 标签绕路。

下一步

如果你在用 Bun,现在就可以直接用,无需任何配置:

# index.ts
import readme from './readme.txt' with { type: 'text' };
console.log(readme);
bun run index.ts

如果你在用 Vite,装一个转换插件:

deno add jsr:@nick/vite-import-attributes

然后在 vite.config.ts 里加一行插件,把 with { type: 'text' } 语法转成 Vite 的 ?raw

import viteImportAttributes from 'vite-import-attributes';

export default {
  plugins: [viteImportAttributes()],
};

Chrome 和 Safari 正式支持只是时间问题。这个语法的设计已经非常成熟,Bun 跑了一年半没有 breaking change,Deno 和 Firefox 跟进,方向已经很明确了。

你的项目里,还在用 ?raw 或者 readFileSync 加载文本吗?

评论区

0 条评论

登录后可评论。

阿柯·前端架构 13 阅读