close
CC 4.0 协议

本节内容派生于以下链接指向的内容 ,并遵守 CC BY 4.0 许可证的规定。

以下内容如果没有特殊声明,可以认为都是基于原内容的修改和删减后的结果。

模块方法

当使用 Rspack 打包应用程序时,你可以选择多种模块语法风格,包括 ES modulesCommonJS

尽管 Rspack 支持多种模块语法,但我们还是建议尽量使用一致的语法,以此避免一些奇怪的行为和 bug。

事实上,当距离最近的 package.json 文件中 "type" 字段值为 "module""commonjs" 时,Rspack 会对 .mjs 文件、.cjs 文件或.js 文件进行对应的处理:

  • 文件后缀为 .mjs,或者 package.json"type": "module" 且后缀为 .js 时:
    • 不允许使用 CommonJS,如 requiremodule.exportsexports
    • 引入文件时需指定扩展名,例如,应使用 import './src/App.mjs',而非 import './src/App'
  • 文件后缀为 .cjs,或者 package.json"type": "commonjs" 且后缀为 .js 时:
    • 不允许使用 ESM,如 importexport

ES modules (推荐)

Rspack 原生支持 ES modules 语法,可以使用静态的 importexportimport() 语法。

Warning

如果使用其他的 ES6+ 特性,你仍然需要引入 SWC 或 Babel 进行转换。

import

静态 import 另一个模块的 export

import MyModule from './my-module.js';
import { NamedExport } from './other-module.js';

你还可以 import Data URI,这允许你直接在导入语句中嵌入 Base64 编码的 JavaScript 代码:

// 等同于 import 一个包含 `console.log('hello')` 的模块
import 'data:text/javascript;charset=utf-8;base64,Y29uc29sZS5sb2coJ2hlbGxvJyk=';

// 等同于 import 一个包含 `export const number = 42;` 的模块
import { number } from 'data:text/javascript;charset=utf-8;base64,ZXhwb3J0IGNvbnN0IG51bWJlciA9IDQyOw==';

export

将任何内容作为默认导出或具名导出。

// 具名导出
export var Count = 5;
export function Multiply(a, b) {
  return a * b;
}

// 默认导出
export default {
  // Some data...
};

Dynamic import()

function import(path: string): Promise;

动态加载模块,参考 Dynamic import 了解更多。

import() 的调用被视为分割点,这意味着请求的模块及其子模块被拆分成单独的 chunk。

if (module.hot) {
  import('lodash').then(_ => {
    // 使用 lodash 做些事情...
  });
}
Warning

此功能内部依赖于 Promise。如果你在旧版浏览器中使用 import(),请记得使用类似于 core-jses6-promisepromise-polyfill 的 polyfill 来模拟 Promise

import() 中的动态表达式

import() 无法使用完全动态的导入语句,例如 import(foo)。因为 foo 可能是系统或项目中任何文件的任何路径。

import() 必须至少包含一些关于模块所在位置的信息。可以将打包限制在特定目录或一组文件中,这样当你使用动态表达式时,在 import() 调用中可能被请求的每个模块都会被包括进来。

例如,import(./locale/${language}.json) 会将 ./locale 目录中的每个 .json 文件都被打包进新的代码块。在运行时,一旦变量 language 被计算出来,任何像 english.jsongerman.json 这样的文件都将可供使用。

// 想象一下,如果我们有一种方法可以从 Cookie 或其他存储中获取语言
const language = detectVisitorLanguage();
import(`./locale/${language}.json`).then(module => {
  // 做一些翻译相关的事情
});

Magic comments

Rspack/Webpack specific

通过向 import 语句添加注释,我们可以实现「指定 chunk 名称」或「设置加载模式」等操作。有关这些魔法注释的完整列表,请参见下面的代码,以及对这些注释功能的解释。

// 单个模块
import(
  /* webpackChunkName: "my-chunk-name" */
  /* webpackMode: "lazy" */
  /* webpackFetchPriority: "high" */
  'module'
);

// 多个可能模块
import(
  /* webpackInclude: /\.json$/ */
  /* webpackExclude: /\.noimport\.json$/ */
  /* webpackChunkName: "my-chunk-name" */
  /* webpackMode: "lazy" */
  /* webpackPrefetch: true */
  /* webpackPreload: true */
  `./locale/${language}`
);
webpackIgnore
  • 类型: boolean

设置为 true 时,Rspack 将跳过对该动态导入的静态分析和打包处理。具体表现为:

  1. 构建时不会分析该模块的依赖关系
  2. 该模块不会被打包为独立的 chunk 文件
  3. 导入操作将在运行时由浏览器原生 import() 执行

这在需要从外部 CDN 动态加载 ESM 格式的第三方库时特别有用,例如:

import('https://esm.sh/react' /* webpackIgnore: true */).then(React => {
  console.log(React.version); // 19.0.0
});

webpackIgnore 同样适用于 new URL() 语法。默认情况下,Rspack 会解析 new URL() 中的模块标识符,并将其引用的模块打包到构建产物中。当你需要 Rspack 跳过某个 new URL() 的处理时,可以添加 webpackIgnore: true 注释:

// Rspack 会处理这个 URL,将 './index.css' 打包到构建产物中
const url1 = new URL('./index.css', import.meta.url);

// Rspack 会忽略这个 URL,保持原始的模块标识符
const url2 = new URL(/* webpackIgnore: true */ './index.css', import.meta.url);
webpackMode
  • 类型: "eager" | "lazy" | "weak" | "lazy-once"
  • 默认值: 'lazy'

可以指定以不同的模式解析动态导入。支持以下选项:

  • 'lazy' (默认值):为每个 import() 导入的模块生成一个可延迟加载(lazy-loadable)的 chunk。
  • 'lazy-once':生成一个可以满足所有 import() 调用的单个可延迟加载(lazy-loadable)的 chunk。此 chunk 将在第一次 import() 时调用时获取,随后的 import() 则使用相同的网络响应。注意,这种模式仅在部分动态语句中有意义,例如 import("./locales/${language}.json"),其中可能含有多个被请求的模块路径。
  • 'eager':不会生成额外的 chunk。所有的模块都被当前的 chunk 引入,并且没有额外的网络请求。但是仍会返回一个 resolved 状态的 Promise。与静态导入相比,在调用 import() 完成之前,该模块不会被执行。
  • 'weak':尝试加载模块,如果该模块函数已经以其他方式加载,(即另一个 chunk 导入过此模块,或包含模块的脚本被加载)。仍会返回 Promise, 但是只有在客户端上已经有该 chunk 时才会成功解析。如果该模块不可用,则返回 rejected 状态的 Promise,且网络请求永远都不会执行。当需要的 chunks 始终在(嵌入在页面中的)初始请求中手动提供,而不是在应用程序导航在最初没有提供的模块导入的情况下触发,这对于通用渲染(SSR)是非常有用的。
webpackPrefetch
  • 类型:
    • number: 预取优先级
    • boolean: false 表示不预取, true 表示预取并且优先级是 0

告诉浏览器将来可能需要该资源来进行某些导航跳转,详情请查看预取/预载模块

webpackPreload
  • 类型:
    • number: 预载优先级
    • boolean: false 表示不预载, true 表示预载并且优先级是 0

告诉浏览器在当前导航期间可能需要该资源,详情请查看预取/预载模块

webpackChunkName
  • 类型:: string

指定新 chunk 的名称。

webpackFetchPriority
  • 类型:: "low" | "high" | "auto"

为指定的动态导入设置 fetchPriority。也可以通过使用 module.parser.javascript.dynamicImportFetchPriority 选项为所有动态导入设置全局默认值。

webpackInclude
  • 类型:: Regexp

在导入解析时匹配的正则表达式。只有匹配的模块才会被打包

webpackExclude
  • 类型:: Regexp

在导入解析时匹配的正则表达式。只有匹配的模块不会被打包

Info

请注意,webpackIncludewebpackExclude 选项不会影响前缀。例如:./locale

webpackExports
  • Type:: string | string[]

使 Rspack 在处理该动态 import() 模块时仅打包指定的导出。这样可以降低 chunk 的产物体积。

CommonJS

Rspack 也支持 CommonJS 语法,可以使用 requiremodule.exports 语法。

require

以同步的方式引入其他模块。

require(dependency: string);

require.resolve

以同步的方式获取模块的 ID。此方法会将模块打包进最终的 bundle 中,但不会执行模块代码。返回的模块 ID 主要用于与 require.cache[id] 或 Rspack 内部的 __webpack_require__(id) 一起使用,在大多数应用场景中应避免直接使用此方法。

require.resolve(dependency: string);
Warning

模块 ID 的类型可以是 numberstring,具体取决于 optimization.moduleIds 配置。

require.cache

多处引用同一模块,最终只会产生一次模块执行和一次导出。所以,会在运行时(runtime)中会保存一份缓存。删除此缓存,则会产生新的模块执行和新的导出。

var d1 = require('dependency');
require('dependency') === d1;
delete require.cache[require.resolve('dependency')];
require('dependency') !== d1;

require.context

Rspack/Webpack specific

require.context 允许你动态地 require 一组模块。

你可以在代码中使用 require.context,Rspack 将在构建时进行解析并引用匹配的模块。

Tip

require.context 的返回值与 import.meta.webpackContext 相同,我们推荐使用 import.meta.webpackContext,它的功能更加强大。

  • 类型:
function requireContext(
  /**
   * 要搜索的目录
   */
  directory: string,
  /**
   * 是否还搜索其子目录
   * @default true
   */
  includeSubdirs?: boolean,
  /**
   * 匹配文件的正则表达式
   * @default /^\.\/.*$/(匹配任意文件)
   */
  filter?: RegExp,
  /**
   * 模块加载模式
   * @default 'sync'
   */
  mode?: 'sync' | 'eager' | 'weak' | 'lazy' | 'lazy-once',
): Context;
  • 示例:
// 创建一个上下文,
// 文件直接来自 test 目录,匹配的文件名以 `.test.js` 结尾。
const context = require.context('./test', false, /\.test\.js$/);
// 创建一个上下文,
// 文件来自父文件夹及其所有子级文件夹,匹配的文件名以 `.stories.js` 结尾。
const context = require.context('../', true, /\.stories\.js$/);
// 如果 `mode` 被设置为 `lazy`,模块将会被异步加载
const context = require.context('./locales', true, /\.json$/, 'lazy');
Tip

Rspack 在编译时,会通过静态分析来解析 require.context 的参数,因此参数必须是字面量

例如,filter 的值不允许传入一个变量,也不允许传入 new RegExp() 生成的值,只能是一个正则表达式字面量。

require.ensure

Rspack/Webpack specific
Tip

require.ensure() 基本已经被 import() 取代.

将给定 dependencies 单独打包,并异步加载。当使用 CommonJS 语法的时候,这是唯一动态加载依赖的方式。同时,这个 API 也可以用于运行时,只有在满足某些条件时才会加载依赖。

Warning

该特性内部依赖于 Promise。 如果你在旧的浏览器上使用 require.ensure, 最好加入 polyfill es6-promisepromise-polyfill

  • Type:
function requireEnsure(
  /**
   * callback 执行需要的模块列表
   */
  dependencies: String[],
  /**
   * 当 dependencies 中的模块都加载完毕时,这个函数就会被执行。
   * require 函数将作为第一个参数传入该函数。
   * 在这个函数中也可以进一步使用 require() 来加载模块。
   */
  callback: function(require),
  /**
   * 当加载依赖失败的时候执行的函数
   */
  errorCallback?: function(error),
  /**
   * 指定由 require.ensure() 生成的 chunk 名称。
   * 通过指定相同的 chunkName 来合并多个 require.ensure() 调用的代码,从而只生成一个 bundle,
   * 从而减少浏览器加载的次数。
   */
  chunkName?: string
): Context;
  • Example:
var a = require('normal-dep');

if (module.hot) {
  require.ensure(['b'], function (require) {
    var c = require('c');

    // Do something special...
  });
}

require.resolveWeak

Rspack/Webpack specific

require.resolve 类似,但是不会把 module 打包到最终的 bundle 中。是 "弱(weak)" 依赖。

require.resolveWeak(dependency: string);

示例:

if (__webpack_modules__[require.resolveWeak('module')]) {
  // 当模块可用时,执行一些操作
}
if (require.cache[require.resolveWeak('module')]) {
  // 在模块加载完成之前,执行一些操作
}

// 你可以像执行其他 require/import 方法一样,
// 执行动态解析上下文 resolves ("context")
const page = 'Foo';
__webpack_modules__[require.resolveWeak(`./page/${page}`)];

Data URI 模块

Rspack 支持使用 importrequire 语法导入 Data URI 模块。

  • import
import DataURI from 'data:text/javascript,export default 42';
  • require
require('data:text/javascript,module.exports = 42');

除此之外,还支持了 Base64 编码:

const {
  number,
  fn,
} = require('data:text/javascript;charset=utf-8;base64,ZXhwb3J0IGNvbnN0IG51bWJlciA9IDQyOwpleHBvcnQgZnVuY3Rpb24gZm4oKSB7CiAgcmV0dXJuICJIZWxsbyB3b3JsZCI7Cn0=');
Tip

Data URI 模块可以被用作虚拟模块(Virtual Modules)的实现方式,如:配合 loader 完成运行时动态加载自定义模块。

内置 Rules

Rspack 默认支持这些 MIME 类型 的 data URI 模块:application/jsontext/javascriptapplication/javascriptapplication/nodeapplication/wasm。这意味着当你使用这些 MIME 类型创建 data URI 模块时,Rspack 会自动识别他们。

Rspack 内置的 MIME rules 如下:

const defaultMimeRules = [
  {
    mimetype: 'application/node',
    type: 'javascript/auto',
  },
  {
    mimetype: 'application/json',
    type: 'json',
  },
  {
    mimetype: {
      or: ['text/javascript', 'application/javascript'],
    },
    type: 'javascript/esm',
    // ...
  },
  {
    mimetype: 'application/wasm',
    type: 'webassembly/async',
    // ...
  },
];

自定义 Rules

你可以也使用 Rule.mimetype 来扩展 Data URI 模块的匹配规则,例如为 text/javascript 添加自定义的 loaders:

rspack.config.mjs
export default {
  module: {
    rules: [
      {
        mimetype: 'text/javascript',
        use: [
          // ...
        ],
      },
    ],
  },
};