close
  • 简体中文
  • Wasm

    Rslib 原生支持 WebAssembly(WASM)模块,允许你在项目中通过 WebAssembly ESM Integration 提案定义的 ESM 导入方式,直接导入和使用 .wasm 文件。

    Note

    使用 WebAssembly ESM Integration 导入 .wasm 模块时,仅支持生成 ESM 格式的产物,因此 format 必须设置为 'esm'(默认值)。

    使用 WASM 模块

    Rslib 支持通过以下方式使用 .wasm 模块。

    静态导入与重新导出

    你可以使用标准 ESM 语法导入 .wasm 模块并访问其实例化后的导出,也可以直接重新导出这些内容:

    import { add } from './add.wasm';
    import * as wasm from './add.wasm';
    import './add.wasm';
    
    export { add } from './add.wasm';
    export * from './add.wasm';
    export * as wasm from './add.wasm';

    动态导入

    你可以使用 import() 动态导入 .wasm 模块并访问其实例化后的导出:

    const wasm = await import('./add.wasm');

    Source phase 导入

    你可以使用 Source Phase Imports 获取编译后的 WebAssembly.Module,并通过自定义 import object 手动完成实例化:

    import source addModule from './add.wasm';
    
    const { instance } = await WebAssembly.instantiate(addModule, {
      env: { now: Date.now },
    });

    你也可以使用 import.source() 动态获取编译后的 WebAssembly.Module

    const addModule = await import.source('./add.wasm');
    Note

    TypeScript 目前无法解析 import sourceimport.source()。请在 JavaScript 文件中使用这些语法,或选择支持它们的工具链。

    输出模式

    你可以通过 lib.wasm.mode 选择 .wasm 模块的输出模式:

    • bundletrue:仅支持 compile 模式。
    • bundlefalse:默认使用 preserve 模式,适用于 .wasm 模块由支持 WebAssembly ESM Integration 的下游构建工具(如 Rsbuild 或 Rspack)或目标运行时(如 Node.js >=24.5.0)解析和加载的场景。如果消费方不支持该特性,或不希望依赖其处理 .wasm 模块,则使用 compile 模式。

    以下通过一组示例源码,介绍 compile 和 preserve 模式的配置方式及构建结果。

    src/index.ts
    src/utils.ts
    src/add.wasm
    export { useAdd } from './utils.js';

    compile 模式

    Rslib 解析每个 .wasm 模块,生成加载和实例化该模块所需的 JavaScript 胶水代码,并将二进制文件作为静态资源输出到由 output.distPath.wasm 指定的目录(默认为 dist/static/wasm),文件名包含 content hash。

    根据配置文件中的 bundle 配置,在 dist 目录下输出如下产物:

    bundle
    bundleless
    index.js
    static/wasm/[contenthash].module.wasm
    function __webpack_require__(moduleId) {
      // 读取缓存并执行已注册的模块...
    }
    
    __webpack_require__.add = (modules) => {
      // 注册模块...
    };
    __webpack_require__.v = async (
      exports,
      wasmModuleId,
      wasmModuleHash,
      importsObj,
    ) => {
      // 根据 output.target 加载 static/wasm/[contenthash].module.wasm...
      const bytes = await loadWasmBytes(wasmModuleHash);
      const { instance } = await WebAssembly.instantiate(bytes, importsObj);
      return Object.assign(exports, instance.exports);
    };
    
    __webpack_require__.add({
      './src/add.wasm'(module, exports, __webpack_require__) {
        module.exports = __webpack_require__.v(exports, module.id, '[contenthash]');
      },
    });
    
    const add = await __webpack_require__('./src/add.wasm');
    const useAdd = (a, b) => (0, add.add)(a, b);
    export { useAdd };

    生成的加载代码取决于 output.target

    • web:使用 fetch 加载 .wasm 文件。
    • node:使用 Node.js 异步文件系统 API 加载 .wasm 文件。

    preserve 模式

    Rslib 在 JavaScript 产物中保留对 .wasm 模块的 import 语句,并在 dist 目录下按源码相对路径和原文件名原样输出二进制文件:

    index.js
    utils.js
    add.wasm
    export { useAdd } from './utils.js';

    preserve 模式会保留 .wasm 文件的原始名称,因此 output.filenameHash 不会影响其文件名。

    JavaScript 产物的路径和文件名限制

    Rslib 会更新 JavaScript 产物中对 .wasm 文件的 import,使其指向输出后的文件,但不会更新 .wasm 二进制内部记录的 import 模块名。

    如果这些模块名对应 JavaScript 产物,请勿通过以下配置改变 JavaScript 产物的相对路径或文件名:

    与 wasm-bindgen 配合使用

    wasm-bindgen 是使用 Rust 构建 WebAssembly 库的工具,可通过 --target 选项生成面向不同运行环境的产物。

    Rslib 目前支持以下 target:

    • bundler(推荐):胶水 JavaScript 以 ES module 形式导入 .wasm 模块,同时提供该模块实例化所需的 imports。compilepreserve 两种模式均可使用;使用 preserve 模式时,需遵守上文的路径和文件名限制
    • module:胶水 JavaScript 通过 Source Phase 导入获取编译后的 WebAssembly.Module,然后构造 import object 并完成实例化。compilepreserve 两种模式均可使用。
    • web / experimental-nodejs-module:胶水 JavaScript 通过 new URL('./pkg.wasm', import.meta.url) 定位并加载 .wasm 文件。Rslib 将该文件作为静态资源处理,因此 lib.wasm.mode 不适用。

    类型声明

    TypeScript 未内置 .wasm 文件的模块声明。使用 TypeScript 时,你需要在 .wasm 文件旁添加一个 .d.wasm.ts 声明文件,并在 tsconfig.json 中启用 allowArbitraryExtensions

    src/add.d.wasm.ts
    export function add(a: number, b: number): number;