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

# dev.lazyCompilation

- **类型：**

```ts
type LazyCompilationOptions =
  | boolean
  | {
      /**
       * 为 entries 启用 lazy compilation
       */
      entries?: boolean;
      /**
       * 为 dynamic imports 启用 lazy compilation
       */
      imports?: boolean;
      /**
       * 指定哪些导入的模块应该被延迟编译
       */
      test?: RegExp | ((m: Module) => boolean);
      /**
       * 指定一个自定义的运行时代码路径，用于覆盖默认的 lazy compilation client
       */
      client?: string;
      /**
       * 指定 client 需要请求的 server URL
       */
      serverUrl?: string;
      /**
       * 自定义 lazy compilation URL 的前缀
       * @default "/lazy-compilation-using-"
       */
      prefix?: string;
      /**
       * 为 Node 目标启用 lazy compilation
       * 应用需要保留并更新输出的 runtime
       * @default false
       */
      node?: boolean;
    };
```

- **默认值：**

```js
const defaultOptions = {
  imports: true,
  entries: false,
};
```

用于开启 lazy compilation（即按需编译），基于 Rspack 的 [lazy compilation](https://rspack.rs/zh/guide/advanced/lazy-compilation) 特性实现。

## 介绍

尽管 Rspack 本身具备良好的性能，但是面对具有大量模块的应用，其整体构建时间仍然可能不够理想。这是因为应用中的模块需要经过不同 loader 的编译，包括 `postcss-loader`、`sass-loader`、`vue-loader` 等，它们都会产生额外的编译开销。

Lazy compilation 是一个提升开发阶段启动性能的有效手段，它采用按需编译模块的方式，而非在启动时编译全部模块。这意味着开发者在启动 dev server 时，可以很快看到应用运行，并分次构建所需的模块。通过这种按需编译的机制，可以减少不必要的编译时间，且随着项目规模的扩大，编译时间不会显著增长，从而大幅提升开发体验。

:::tip
Lazy compilation 仅在开发阶段有效，对于生产构建不会产生影响。
:::

## 示例

### 启用 Lazy compilation

对于 web 目标，Rsbuild 默认开启 `imports` 选项，即 lazy 编译动态引入的模块。

将 `lazyCompilation` 设置为 `true` 即可开启完整的 lazy compilation 功能：

```ts title="rsbuild.config.ts"
export default {
  dev: {
    lazyCompilation: true,
  },
};
```

这等价于以下配置：

```ts title="rsbuild.config.ts"
export default {
  dev: {
    lazyCompilation: {
      imports: true,
      // 如果只有一个入口，则 Rsbuild 默认不启用 entries 选项
      entries: true,
    },
  },
};
```

### 禁用 Lazy compilation

将 `lazyCompilation` 设置为 `false` 来禁用 lazy compilation:

```ts title="rsbuild.config.ts"
export default {
  dev: {
    lazyCompilation: false,
  },
};
```

### 入口模块

通过 `lazyCompilation.entries` 来控制是否延迟编译入口模块：

```ts title="rsbuild.config.ts"
export default {
  dev: {
    lazyCompilation: {
      entries: true,
    },
  },
};
```

在启用 `entries` 选项后，当你启动 dev server 时，Rsbuild 不会编译所有的页面，而是仅在访问特定的页面时，才对该页面进行编译。

延迟编译入口模块时，有以下注意事项：

- 只适用于多页应用（MPA），对单页应用（SPA）没有优化效果。
- 当你访问一个页面时，你需要等待页面编译完成才能看到页面的内容。

### 异步模块

通过 `lazyCompilation.imports` 来控制是否延迟编译 [dynamic import](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import) 引入的异步模块：

```ts title="rsbuild.config.ts"
export default {
  dev: {
    lazyCompilation: {
      imports: true,
    },
  },
};
```

开启 `imports` 选项后，所有的异步模块只有在被请求时才触发编译。如果你的项目是一个单页应用（SPA），并通过 dynamic import 进行了路由拆分，那么 dev 启动时间会有明显提升。

### Server URL

通过 [lazyCompilation.serverUrl](https://rspack.rs/zh/config/lazy-compilation#serverurl) 可以指定 client 需要请求的 server URL。

对于 web 目标，如果未显式配置 `lazyCompilation.serverUrl`，Rsbuild 会在同时满足以下条件时，根据 [dev.client](/zh/config/dev/client.md) 推导默认的 `serverUrl`：

- [dev.assetPrefix](/zh/config/dev/asset-prefix.md) 为 `true` 或绝对 URL。
- 配置了 [dev.client.host](/zh/config/dev/client.md#host) 或 [dev.client.port](/zh/config/dev/client.md#port)。

当 `dev.assetPrefix` 为相对 URL 时，Rsbuild 不会根据 `dev.client` 推导 `serverUrl`。懒编译请求会发送到当前页面所在的源（origin）。

如果显式配置了 `lazyCompilation.serverUrl`，该配置始终优先于从 `dev.client` 推导出的默认值。

示例如下：

```ts title="rsbuild.config.ts"
export default {
  dev: {
    lazyCompilation: {
      serverUrl: 'http://localhost:<port>',
    },
  },
};
```

:::tip
Rsbuild 会自动将 `<port>` 占位符替换为 server 实际监听的端口号。
:::

### Node 目标

Node 目标默认立即编译。设置 `dev.lazyCompilation.node: true` 可延迟编译动态导入的模块。布尔值 `true` 和未设置 `node: true` 的配置仍仅作用于浏览器目标。Node 入口始终立即编译，设置 `entries: true` 会报错。Node lazy compilation 要求 `mode: 'development'` 和 `dev.hmr: true`。其他模式会报错，因为热更新必须保留运行时实例。

例如，为应用自行管理的原生 runtime 配置 ES module bundle：

```ts title="rsbuild.config.ts"
export default {
  output: {
    target: 'node',
    module: true,
    filename: {
      js: '[name].mjs',
    },
  },
  dev: {
    writeToDisk: true,
    lazyCompilation: { node: true },
  },
  tools: {
    rspack: {
      output: { publicPath: 'auto' },
    },
  },
};
```

应用或框架负责 runtime 的生命周期：

- 使用原生 `import()` 或 `require()` 加载一次输出的入口，并在 lazy import 等待期间保留该 runtime。
- 从入口暴露调用 `import.meta.webpackHot.check(true)` 或 `module.hot.check(true)` 的接口。在编译成功、更新文件可用后调用该接口。按顺序应用待处理的更新，直到运行时的 `__webpack_hash__` 与成功编译的 hash 一致。Rsbuild 会安装 HMR 插件，但不会主动执行 hot check。
- 处理更新失败，并在关闭时释放 runtime、请求和 server。应用需要自行恢复 runtime 无法接受的更新。

原生 runtime 需要访问输出的 chunk 和 hot-update 文件。示例将这些文件写入磁盘，并通过 `publicPath: 'auto'` 让 ESM hot update 相对于输出目录解析。在 `"type": "module"` 的 package 中使用 CommonJS 输出时，应为 bundle 和异步 chunk 设置 `.cjs` 文件名，并将 `tools.rspack.output.hotUpdateChunkFilename` 设置为 `'[id].[fullhash].hot-update.cjs'`。

Node 激活端点默认使用 dev server 的协议、主机和实际端口生成绝对 HTTP(S) URL，不受 `dev.client` 配置影响。显式设置的 HTTP(S) `serverUrl` 优先，并支持 `<port>`。使用外部托管的 dev middleware 时，应将 `serverUrl` 设置为可访问的宿主端点。

Runtime 的管理方还需要定义由哪个模块接受更新。模块接受自身更新时不会通知父模块，因此先前返回的模块引用可能保持不变。Lazy boundary 可以接受静态依赖的更新，并通过 getter 读取更新后的 binding：

```js title="src/boundary.js"
import { value } from './value.js';

export const getValue = () => value;

import.meta.webpackHot.accept('./value.js', () => {});
```

入口可以只 lazy 导入一次该 boundary，并在更新后调用 `getValue()`。发生变化的 `value.js` 模块需要将更新传播到 boundary，而不是接受自身更新。

Rsbuild 不会保留或热更新 `environment.loadBundle` 创建的 runtime。Node 输出仍不通过 HTTP 提供为静态资源。框架需要协调接受更新的模块边界、路由 manifest、loader 更新与保留的 runtime。

## 版本历史

| 版本      | 变更内容                                                                    |
| ------- | ----------------------------------------------------------------------- |
| v1.3.0  | 新增该选项                                                                   |
| v1.5.0  | 默认值由 `false` 改为 `{ imports: true, entries: false }`                     |
| v2.0.13 | 根据 `dev.client` 推导默认的 `serverUrl`                                       |
| v2.1.7  | 仅当 `dev.assetPrefix` 为 `true` 或绝对 URL 时，才根据 `dev.client` 推导 `serverUrl` |
