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

# Web Workers

Rspack 提供了对 [Web Workers](https://developer.mozilla.org/zh-CN/docs/Web/API/Web_Workers_API/Using_web_workers) 的内置支持，这意味着你不需要任何的 Loader 就可以直接使用 Web Workers。

## 使用方式

基本的 Worker 创建语法：

```js
new Worker(new URL('./worker.js', import.meta.url));
```

通过 `name` 属性可以自定义 Worker 的 chunk 名称（需要属性值可被静态分析），此名称会替换生成的 chunk 文件名中的 `[name]` 占位符：

```js
new Worker(new URL('./worker.js', import.meta.url), {
  name: 'my-worker',
});
```

:::info
选择该语法的原因在于其具备良好的标准兼容性，它基于标准的 ECMAScript 模块规范，即使在没有构建工具的环境中也能正常运行。例如，它可以直接在支持 ES modules 的现代浏览器中执行。
:::

使用示例可参考：

- [examples/worker](https://github.com/rstackjs/rstack-examples/tree/main/rspack/worker)
- [examples/monaco-editor-js](https://github.com/rstackjs/rstack-examples/tree/main/rspack/monaco-editor-js)
- [examples/monaco-editor-ts-react](https://github.com/rstackjs/rstack-examples/tree/main/rspack/monaco-editor-ts-react)

### 限制

1. 由于 Rspack 依赖静态分析来识别 worker 的使用，因此它有以下两个限制：
   - `new Worker` 以及其他类似 worker 的 API 通常也可以接受 URL 的字符串表示，但 Rspack 只支持传入 URL 对象。实际使用中，你必须传入 `new URL('./worker.js', import.meta.url)`。
   - Rspack 无法静态分析 `new Worker` 中使用的变量。例如，以下代码将不起作用：

     ```js
     const url = new URL('./path/to/worker.js', import.meta.url);
     const worker = new Worker(url);
     ```

2. `/* webpackEntryOptions: { filename: "workers/[name].js" } */` 魔法注释目前仍不支持。

## 内置语法

除 `new Worker()` 以外，Rspack 还默认支持以下内置形式：

- `new SharedWorker()`，详见 [SharedWorker](https://developer.mozilla.org/en-US/docs/Web/API/SharedWorker)。

```js
const sharedWorker = new SharedWorker(
  new URL('./shared-worker.js', import.meta.url),
);
sharedWorker.port.start();
```

- `import { Worker } from "worker_threads"`：常用于 Node.js 环境，详见 [Worker threads](https://nodejs.org/api/worker_threads.html)。

```js
import { Worker } from 'worker_threads';

const worker = new Worker(new URL('./node-worker.js', import.meta.url));
```

- `navigator.serviceWorker.register()`：用于注册 Service Worker，详见 [ServiceWorkerContainer](https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerContainer/register)。

```js
navigator.serviceWorker
  .register(new URL('./sw.js', import.meta.url))
  .then((registration) => {
    console.log('Service worker registration succeeded:', registration);
  });
```

## 自定义语法

可以通过 [`module.parser.javascript.worker`](/zh/config/module-parser.md#javascriptworker) 告诉 Rspack 哪些表达式要按 Worker 类入口语法处理：

- `true` 保留内置默认值，等价于 `['...']`。
- `false` 关闭 worker 语法分析，等价于 `[]`。
- 数组会替换默认值；如果你想在内置默认值的基础上扩展，记得保留 `'...'`。

内置语法也是通过同一套机制实现的。`'...'` 这个占位符可以展开为以下条目：

```js title="rspack.config.mjs"
export default {
  module: {
    parser: {
      javascript: {
        worker: [
          'Worker',
          'SharedWorker',
          'navigator.serviceWorker.register()',
          'Worker from worker_threads',
        ],
      },
    },
  },
};
```

下面这些规则说明了自定义语法的含义：

- 末尾带 `()` 表示匹配调用语法；末尾不带 `()` 表示匹配构造器语法。
  - `'worker'` 会匹配 `new worker(new URL("./worker.js", import.meta.url))`。
  - `'worker()'` 会匹配 `worker(new URL("./worker.js", import.meta.url))`。
- 也支持成员语法，但根对象必须是一个自由变量。
  - `'foo.bar'` 会匹配 `new foo.bar(new URL("./worker.js", import.meta.url))`。
  - `'foo.bar()'` 会匹配 `foo.bar(new URL("./worker.js", import.meta.url))`。
- 可以使用 `*` 按变量名精确匹配根对象。和普通成员语法不同，这里的根对象是一个已声明变量，而不是自由变量。
  - `'*foo.bar()'` 会匹配 `const foo = new WorkletContext(); foo.bar(new URL("./processor.js", import.meta.url))`。
  - 目前仅支持调用语法，构造器语法如 `'*foo.bar'` 暂不支持。
- 可以使用 `from` 来匹配来自某个精确 ESM source 的导入绑定。
  - `'Foo from pkg'` 会匹配 `import { Foo } from 'pkg'; new Foo(new URL("./worker.js", import.meta.url))`。
  - `'default from pkg'` 会匹配 `import Foo from 'pkg'; new Foo(new URL("./worker.js", import.meta.url))`。
  - `'foo() from pkg'` 会匹配 `import { foo } from 'pkg'; foo(new URL("./worker.js", import.meta.url))`。

```js title="rspack.config.mjs"
export default {
  module: {
    parser: {
      javascript: {
        worker: [
          // 自定义语法
          'MyWorker',
          'myWorker()',
          'CSS.paintWorklet.addModule()',
          '*audioContext.audioWorklet.addModule()',
          'default from web-worker',
          // 扩展内置语法
          '...',
        ],
      },
    },
  },
};
```

下面的示例展示了这些自定义语法规则在实际中的用法。

前几个示例基于内置语法。实际使用中，你不需要把它们再写进配置里，因为它们已经包含在默认的 `'...'` 中。这里展示它们只是为了说明自定义语法规则是如何工作的：

- 内置构造器语法：`Worker` 和 `SharedWorker`

  `Worker` 和 `SharedWorker` 都是构造器，而不是函数，因此对应的语法字符串末尾不需要带 `()`。

  ```js
  const worker = new Worker(new URL('./worker.js', import.meta.url));

  const sharedWorker = new SharedWorker(
    new URL('./shared-worker.js', import.meta.url),
  );
  ```

- 内置调用语法：`navigator.serviceWorker.register()`

  这个例子展示了为什么末尾的 `()` 很重要：`register()` 是函数调用，而不是构造器。

  ```js
  await navigator.serviceWorker.register(new URL('./sw.js', import.meta.url));
  ```

- 内置导入语法：`Worker from worker_threads`

  这个例子展示了 `from` 规则。import source 必须精确匹配。

  ```js
  import { Worker } from 'worker_threads';

  const worker = new Worker(new URL('./node-worker.js', import.meta.url));
  ```

下面这些示例展示了一些生态中常见、可以通过自定义语法支持的模式：

- CSS Worklet

  `CSS.paintWorklet.addModule()` 是一个直接的成员函数调用，因此配置字符串末尾需要带 `()`。

  ```js title="rspack.config.mjs"
  export default {
    module: {
      parser: {
        javascript: {
          worker: [
            'CSS.paintWorklet.addModule()',
            'CSS.layoutWorklet.addModule()',
            'CSS.animationWorklet.addModule()',
            '...',
          ],
        },
      },
    },
  };
  ```

  ```js
  await CSS.paintWorklet.addModule(
    new URL('./paint-worklet.js', import.meta.url),
  );

  await CSS.layoutWorklet.addModule(
    new URL('./layout-worklet.js', import.meta.url),
  );

  await CSS?.animationWorklet?.addModule(
    new URL('./animation-worklet.js', import.meta.url),
  );
  ```

- AudioWorklet

  和 CSS Worklet 不同，`audioContext.audioWorklet.addModule(...)` 中的根对象不是自由变量，而是由 `const audioContext = new AudioContext();` 声明出来的变量。因此，自定义语法需要以 `*` 开头，用来表示根对象是一个已声明变量。

  ```js title="rspack.config.mjs"
  export default {
    module: {
      parser: {
        javascript: {
          worker: ['*audioContext.audioWorklet.addModule()', '...'],
        },
      },
    },
  };
  ```

  ```js
  const audioContext = new AudioContext();

  await audioContext.audioWorklet.addModule(
    new URL('./noise-processor.js', import.meta.url),
  );
  ```

- 从第三方包导入 worker

  下面这些例子展示了 `from` 在不同导入方式下是如何工作的。

  ```js title="rspack.config.mjs"
  export default {
    module: {
      parser: {
        javascript: {
          worker: [
            'default from web-worker',
            'createWorker() from @myorg/worker',
            '...',
          ],
        },
      },
    },
  };
  ```

  ```js
  import Worker from 'web-worker';

  const browserWorker = new Worker(
    new URL('./browser-worker.js', import.meta.url),
  );
  ```

  ```js
  import { createWorker } from '@myorg/worker';

  const helperWorker = createWorker(
    new URL('./helper-worker.js', import.meta.url),
  );
  ```

## worker-loader

:::warning
`worker-loader` 仅作为项目迁移到 Rspack 的临时便捷方案，推荐使用 `new Worker()` 语法。
:::

Rspack 也支持了 worker-loader，不过由于 [worker-loader](https://github.com/webpack-contrib/worker-loader) 已不再维护，请使用 [worker-rspack-loader](https://github.com/rstackjs/worker-rspack-loader) 进行替换。

使用 [resolveLoader](/zh/config/resolve-loader.md) 替换 worker-loader 为 worker-rspack-loader：


**ESM**

```js title="rspack.config.mjs"
import { createRequire } from 'node:module';

const require = createRequire(import.meta.url);

export default {
  resolveLoader: {
    alias: {
      'worker-loader': require.resolve('worker-rspack-loader'),
    },
  },
};
```


**CJS**

```js title="rspack.config.cjs"
module.exports = {
  resolveLoader: {
    alias: {
      'worker-loader': require.resolve('worker-rspack-loader'),
    },
  },
};
```



本页改编自 [webpack 文档](https://webpack.docschina.org/guides/web-workers/)，遵循 [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/)，且已作修改。

