Node.js、浏览器和 Cloudflare Workers 的wasm引入问题
内容提要
文章比较了不同 JavaScript 环境加载 Wasm 的方式:Cloudflare Workers 可直接 import .wasm,浏览器使用 fetch 配合 instantiateStreaming,Node.js 需读取文件。为实现统一初始化接口,作者通过 process.getBuiltinModule("fs") 动态获取 fs,避免顶层 import 在浏览器和 Workers 中报错,从而统一调用方式。
延伸解读
环境差异带来的加载方式分歧
文章指出,Cloudflare Workers 可直接 import .wasm 文件,浏览器需通过 fetch 配合 instantiateStreaming,而 Node.js 则需从文件系统读取二进制。这种差异导致同一 Wasm 库在不同平台需要不同的初始化代码,增加了维护成本。作者最初为三个环境分别编写了 8 个 .mjs 文件,正是这种分歧的直接体现。
统一初始化接口的设计思路
作者提出让初始化函数只接受两种参数:字符串路径或 Cloudflare Workers 中的 WebAssembly.Module 对象。这样,调用方无需关心底层加载细节,只需传入对应参数即可。这种抽象简化了跨环境使用,但实现时需要解决 Node.js 特有的文件读取问题。
Node.js 文件读取的兼容性挑战
在 Node.js 中读取 .wasm 文件必须引入 node:fs 模块,但顶层 import 在浏览器和 Cloudflare Workers 中会报错,即使使用 typeof process 提前判断环境也无法避免。这导致代码无法在多个环境中通用,成为统一接口的主要障碍。
利用 getBuiltinModule 动态获取 fs
作者发现可以通过 process.getBuiltinModule("fs") 动态获取 fs 模块,从而避免顶层 import。这样,在 Node.js 环境下可以安全地读取文件并编译 Wasm,而在其他环境中则不会执行该分支。最终,初始化函数统一为接受路径或 Module 对象,调用方式在各平台保持一致。
Q&A
Cloudflare Workers 中如何加载 Wasm 模块?
在 Cloudflare Workers 中,可以直接使用 import 语句导入 .wasm 文件,例如:import wasm from "./simple.wasm"; 然后通过 WebAssembly.instantiate(wasm, imports) 来实例化。Cloudflare 会将 .wasm 模块作为 WebAssembly.Module 提供给代码。
浏览器中加载 Wasm 的常见方法是什么?
浏览器通常通过 fetch() 获取 Wasm 文件,再使用 WebAssembly.instantiateStreaming() 进行实例化,例如:const { instance } = await WebAssembly.instantiateStreaming(fetch("./simple.wasm"), imports);
Node.js 中如何加载 Wasm 文件?
Node.js 需要通过文件系统读取 .wasm 二进制文件,然后使用 WebAssembly.instantiate() 进行初始化。.instantiate() 的第一个参数必须是 typed array 或 ArrayBuffer。
为什么在 Node.js 中引入 fs 模块会导致浏览器和 Workers 报错?
因为 JavaScript 的 import 语句必须在顶层使用,而 Node.js 需要 import "node:fs" 来读取 .wasm 文件。如果在顶层引入,在浏览器和 Cloudflare Workers 环境中会因不支持 Node.js 内置模块而报错。即使使用 typeof process 提前判断环境也无法避免,因为 import 是静态的。
如何统一不同 JavaScript 环境下的 Wasm 初始化接口?
可以设计一个初始化函数,只接受两种参数:字符串路径或 WebAssembly.Module 对象。在 Node.js 中,通过 process.getBuiltinModule("fs") 动态获取 fs 模块来读取文件,避免顶层 import。这样,Cloudflare Workers 可以传入 WebAssembly.Module,而浏览器和 Node.js 可以传入路径字符串,实现统一调用。
process.getBuiltinModule 方法有什么作用?
process.getBuiltinModule 是 Node.js 的一个方法,可以动态获取内置模块(如 fs),而无需在顶层使用 import 语句。这样可以在需要时获取模块,避免在浏览器和 Cloudflare Workers 等环境中因顶层 import 而报错,从而实现跨环境兼容。