> For AI agents: the complete documentation index is available at /zh/llms.txt, the full documentation bundle is available at /zh/llms-full.txt.

# Wasm

Rslib 原生支持 WebAssembly（WASM）模块，允许你在项目中通过 [WebAssembly ESM Integration](https://github.com/WebAssembly/esm-integration) 提案定义的 ESM 导入方式，直接导入和使用 `.wasm` 文件。

:::note

使用 WebAssembly ESM Integration 导入 `.wasm` 模块时，仅支持生成 ESM 格式的产物，因此 [format](/zh/config/lib/format.md) 必须设置为 `'esm'`（默认值）。

:::

## 使用 Wasm 模块

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

### 静态导入与重新导出

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

```ts
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` 模块并访问其实例化后的导出：

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

### Source phase 导入

你可以使用 [Source Phase Imports](https://github.com/tc39/proposal-source-phase-imports) 获取编译后的 `WebAssembly.Module`，并通过自定义 import object 手动完成实例化：

```ts
import source addModule from './add.wasm';

const { instance } = await WebAssembly.instantiate(addModule, {
  env: { now: Date.now },
});
```

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

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

:::note

TypeScript 目前无法解析 `import source` 和 `import.source()`。如果你需要生成类型声明文件，请在 JavaScript 文件中使用这些语法。

:::

## 输出模式

你可以通过 [lib.wasm.mode](/zh/config/lib/wasm.md#wasmmode) 选择 `.wasm` 模块的输出模式：

- [bundle](/zh/config/lib/bundle.md) 为 `true`：仅支持 `compile` 模式。
- [bundle](/zh/config/lib/bundle.md) 为 `false`：默认使用 `preserve` 模式，适用于 `.wasm` 模块由支持 WebAssembly ESM Integration 的下游构建工具（如 Rsbuild 或 Rspack）或目标运行时（如 [Node.js](https://nodejs.org/api/esm.html#wasm-modules) `>=24.5.0`）解析和加载的场景。如果消费方不支持该特性，或不希望依赖其处理 `.wasm` 模块，则使用 `compile` 模式。

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


**src/index.ts**

```ts
export { useAdd } from './utils.js';
```


**src/utils.ts**

```ts
import { add } from './add.wasm';

export const useAdd = (a: number, b: number) => add(a, b);
```


**src/add.wasm**

```wasm
(type (;0;) (func (param i32 i32) (result i32)))
  (func (;0;) (type 0) (param i32 i32) (result i32)
    local.get 0
    local.get 1
    i32.add)
  (export "add" (func 0)))
```


### `compile` 模式

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

根据配置文件中的 [bundle](/zh/config/lib/bundle.md) 配置，在 `dist` 目录下输出如下产物：


**bundle**


**index.js**

```js
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 };
```


**static/wasm/[contenthash].module.wasm**

```wasm
(type (;0;) (func (param i32 i32) (result i32)))
  (func (;0;) (type 0) (param i32 i32) (result i32)
    local.get 0
    local.get 1
    i32.add)
  (export "add" (func 0)))
```



**bundleless**


**index.js**

```js
export { useAdd } from './utils.js';
```


**utils.js**

```js
import { __webpack_require__ } from './rslib-runtime.js';

__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 };
```


**rslib-runtime.js**

```js
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);
};

export { __webpack_require__ };
```


**static/wasm/[contenthash].module.wasm**

```wasm
(type (;0;) (func (param i32 i32) (result i32)))
  (func (;0;) (type 0) (param i32 i32) (result i32)
    local.get 0
    local.get 1
    i32.add)
  (export "add" (func 0)))
```



生成的加载代码取决于 [output.target](/zh/config/rsbuild/output.md#outputtarget)：

- `web`：使用 `fetch` 加载 `.wasm` 文件。
- `node`：使用 Node.js 异步文件系统 API 加载 `.wasm` 文件。

### `preserve` 模式

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


**index.js**

```js
export { useAdd } from './utils.js';
```


**utils.js**

```js
import { add } from './add.wasm';

const useAdd = (a, b) => add(a, b);
export { useAdd };
```


**add.wasm**

```wasm
(type (;0;) (func (param i32 i32) (result i32)))
  (func (;0;) (type 0) (param i32 i32) (result i32)
    local.get 0
    local.get 1
    i32.add)
  (export "add" (func 0)))
```


`preserve` 模式会保留 `.wasm` 文件的原始名称，因此 [output.filenameHash](/zh/config/rsbuild/output.md#outputfilenamehash) 不会影响其文件名。

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

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

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

- [output.distPath.js](/zh/config/rsbuild/output.md#outputdistpath)
- [output.filename.js](/zh/config/rsbuild/output.md#outputfilename)
- [lib.autoExtension](/zh/config/lib/auto-extension.md)

:::

## 内联 Wasm 模块 \{#inline-wasm-modules}


[v1.0.2 新增](https://github.com/web-infra-dev/rslib/releases/tag/v1.0.2)

在导入 `.wasm` 模块时添加 `?inline` query，即可将其二进制内容内联进 JavaScript 产物，Rslib 不会为这些导入输出 `.wasm` 文件：

```js
import { add } from './add.wasm?inline';
```

[Source phase 导入](#source-phase-导入) 暂不支持 `?inline` query。

## 禁用 Wasm 处理


[v1.0.1 新增](https://github.com/web-infra-dev/rslib/releases/tag/v1.0.1)

将 [lib.wasm](/zh/config/lib/wasm.md) 设置为 `false` 时，Rslib 会保留通过 ESM 语法导入的 `.wasm` 模块的原始导入路径，不解析这些模块，也不输出对应的 `.wasm` 文件。你需要自行管理这些文件，并确保导入路径能够从产物中的 JavaScript 文件正确解析。

:::note

- 使用 `?inline` 时，[lib.wasm.mode](/zh/config/lib/wasm.md#wasmmode) 不影响内联行为。当 [lib.wasm](/zh/config/lib/wasm.md) 设置为 `false` 时，Rslib 不会处理该模块，而是将带有 `?inline` 的导入原样保留在产物中。
- 使用 `new URL('./add.wasm', import.meta.url)` 引用 `.wasm` 文件时，该文件由静态资源流程处理，不受 [lib.wasm](/zh/config/lib/wasm.md) 影响。

:::

## 与 wasm-bindgen 配合使用

[`wasm-bindgen`](https://wasm-bindgen.github.io/wasm-bindgen/) 是使用 Rust 构建 WebAssembly 库的工具，可通过 `--target` 选项生成面向不同运行环境的产物。

Rslib 目前支持以下 target：

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

## 类型声明

TypeScript 未内置 `.wasm` 文件的模块声明。使用 TypeScript 时，你需要在 `.wasm` 文件旁添加一个 `.d.wasm.ts` 声明文件，并在 `tsconfig.json` 中启用 [`allowArbitraryExtensions`](https://www.typescriptlang.org/tsconfig/allowArbitraryExtensions.html)：

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

### 内联导入 \{#inline-imports}

使用 TypeScript 时，请先在 `tsconfig.json` 的 `compilerOptions.types` 中添加 `@rslib/core` 提供的 [预设类型](/zh/guide/basic/typescript.md#预设类型)。如果该配置已有其他类型包，请将它追加到现有数组中：

```json title="tsconfig.json"
{
  "compilerOptions": {
    "types": ["@rslib/core/types"]
  }
}
```

然后在 `.wasm` 文件旁创建一个声明文件，并为具体的 `?inline` 导入路径声明命名导出的类型：

```ts title="src/wasm.d.ts"
export {};

declare module './add.wasm?inline' {
  export const add: (a: number, b: number) => number;
}
```

文件开头的 `export {}` 用于将声明文件标记为一个模块，让 TypeScript 能够识别下面的相对路径模块声明。声明后即可使用命名导入：

```ts title="src/index.ts"
import { add } from './add.wasm?inline';
```

直接重新导出 `.wasm` 模块时，生成的类型声明文件会保留 `?inline`。若希望避免这种情况，可以将 `.wasm` 导入保留在内部模块中，并导出封装后的函数或值。
