Import Bytes S2.7
中文标题:导入字节
- 阶段: Stage 2.7
- 状态: 进行中
- ECMAScript 版本: —
- 同步时间: 2026年8月26日
- English original · 官方仓库
该提案添加了使用标准语法 import . with { type: "bytes" } 从文件导入任意字节的能力,返回一个 Uint8Array。它旨在简化跨平台文件读取并启用打包器优化。7。
以下 README 来自上游仓库,其中的阶段或状态标注可能滞后;当前信息以提案概览为准。
导入字节
一个关于在 JavaScript 中导入字节的适度提案。
状态
- 推动者: @styfle
- 作者: @styfle, @guybedford
- 状态: 阶段 2.7
摘要
该提案建立在导入属性和不可变 ArrayBuffer之上,为跨 JavaScript 环境使用统一语法导入任意字节提供能力。
开发者将能够如下导入字节:
返回的字节是一个由不可变 ArrayBuffer支持的 Uint8Array。
注意:在 https://github.com/whatwg/html/issues/9444 中提到了一个类似提案。
动机
与 JSON 模块有用的原因类似,导入原始字节有助于将这种行为扩展到所有文件。该提案提供了一种同构的(isomorphic)方式,无论 JavaScript 环境如何,都能同步或异步读取文件。
例如,开发者可能想要读取 .png 文件来处理图像或 .woff 文件来处理字体,并将字节传递给同构工具(如 satori)。
如今,开发者必须检测平台才能读取字节。
我们可以通过将其转换为一行代码来最大化可移植性并减少样板代码:
使用导入也提供了在使用打包器时进行进一步优化的机会。例如,打包器可以静态分析此导入并以内联 base64 形式嵌入。
提议的语义和互操作性
如果模块导入具有键为 type 且值为 bytes 的属性,宿主必须要么使导入失败,要么将其视为由不可变 ArrayBuffer 支持的 Uint8Array。Uint8Array 对象是模块的默认导出(该模块没有命名导出)。
在浏览器环境中,请求将包含 Accept: */* 和 Sec-Fetch-Dest: bytes 头。响应的 Content-Type 头将被忽略。
在“本地”桌面/服务器/嵌入式环境中,这等同于文件读取。文件扩展名将被忽略。
模块图中针对同一模块的所有导入语句都将求值为同一个 Uint8Array 对象。
常见问题
该提案如何与缓存配合?
type 属性是否成为模块缓存键的一部分,这由宿主决定(与所有导入属性一样)。
例如,import "foo" 和 import "foo" with { type: "bytes" } 在一个宿主中可能返回相同的模块,而在另一个宿主中返回不同的模块。两者都是有效的实现。
但是,动态导入和具有相同 type 的静态导入必须返回相同的模块,无论在哪个宿主中。
参见 https://github.com/styfle/proposal-import-bytes/issues/4 中的讨论
是否有先例?
Deno 2.4 在 2025 年 7 月 添加了对内联 Uint8Array 的支持
Bun 1.1.7 在 2024 年 5 月 添加了类似的功能,用于内联文本字符串
webpack 添加了资源模块,在 4.x 中通过 url-loader 内联 base64 数据 URI,现在在 5.x 中通过 asset/inline
esbuild 添加了二进制加载器 来内联 Uint8Array
Parcel 添加了一种 data url 方案来内联 base64 数据 URI
Moddable 添加了一个 Resource 类来为嵌入式系统内联宿主缓冲区
为什么不是可变的?
可变的可能因几个原因而存在问题:
- 可能需要在内存中保留缓冲区的多个副本,以避免
import(specifier, { type: "json" })和import(specifier, { type: "bytes" })具有不同的底层字节 - 可能导致多个模块导入同一缓冲区并在一个模块中分离(例如
postMessage()或transferToImmutable())时产生意外行为,这会导致另一个模块中的缓冲区也分离 - 可能导致嵌入式系统内存使用过高(不可变可以使用 ROM 代替)
- 可能在跟踪源映射时导致过多内存
- 可能造成一个不可忽视的全局通信渠道
参见 https://github.com/styfle/proposal-import-bytes/issues/2 和 https://github.com/styfle/proposal-import-bytes/issues/5 中的讨论
为什么是 Uint8Array?
Uint8Array 与 Node.js Buffer 兼容,这使其与现有 JavaScript 代码广泛兼容。
为什么不是 ArrayBuffer?
ArrayBuffer 不能直接读取;开发者必须创建视图(如 Uint8Array)来读取数据。提供 Uint8Array 避免了这种额外的工作。
这种模式在 W3C 设计原则中有所定义。
在 https://github.com/styfle/proposal-import-bytes/issues/5 中有一些关于改用 ArrayBuffer 的讨论,然而全体会议达成共识,认为 Uint8Array 更可取,并且该问题中描述的问题仍然可以用 Uint8Array 解决,只要它由不可变 ArrayBuffer 支持。
为什么不是 Blob?
Blob 是 W3C File API 的一部分,不属于 JavaScript,因此将其包含在 TC39 提案中是不可行的。此外,Blob 通常包含 MIME 类型,但本提案忽略类型。
为什么不是 ReadableStream?
ReadableStream 是 WHATWG Streams 的一部分,不属于 JavaScript,因此将其包含在 TC39 提案中是不可行的。此外,目前没有辅助方法可以将流转换为缓冲区,因此这无法解决编写同构 JavaScript 的原始动机。
参见 https://github.com/styfle/proposal-import-bytes/issues/3 中的讨论
为什么不是阶段?
本提案不试图引入新阶段,例如 source 或 asset 提案。
新阶段需要对 JavaScript 进行语法更改,并导致 Sec-Fetch-Dest: script 而不是 bytes。它还带来了安全隐患,因为导入任意代码可能是恶意的。
参见 https://github.com/tc39/proposal-import-bytes/issues/16 中的讨论
为什么是 type: bytes?
type 属性的提出与 type: json 引入的原因相同:将其与代码加载分离,并提供不同的 Sec-Fetch-Dest。
type: bytes 名称对于已经使用 Response.bytes() 和 Blob.bytes() 的开发者来说会很熟悉,它们都返回 Uint8Array。