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:

import 'data:text/javascript;charset=utf-8;base64,Y29uc29sZS5sb2coJ2lubGluZSAxJyk7';
import {
  number,
  fn,
} from 'data:text/javascript;charset=utf-8;base64,ZXhwb3J0IGNvbnN0IG51bWJlciA9IDQyOwpleHBvcnQgY29uc3QgZm4gPSAoKSA9PiAiSGVsbG8gd29ybGQiOw==';

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 时,禁用动态导入解析。

WARNING

当 webpackIgnore 为 true 时,不会将代码分离成为独立的 chunk。

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。可视为只能与 require.cache[id] 或 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...
  });
}

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 完成运行时动态加载自定义模块。