本节内容派生于以下链接指向的内容 ,并遵守 CC BY 4.0 许可证的规定。
以下内容如果没有特殊声明,可以认为都是基于原内容的修改和删减后的结果。
当使用 Rspack 打包应用程序时,你可以选择多种模块语法风格,包括 ES modules 和 CommonJS。
尽管 Rspack 支持多种模块语法,但我们还是建议尽量使用一致的语法,以此避免一些奇怪的行为和 bug。
事实上,当距离最近的 package.json 文件中 "type" 字段值为 "module" 或 "commonjs" 时,Rspack 会对 .mjs 文件、.cjs 文件或.js 文件进行对应的处理:
.mjs,或者 package.json 中 "type": "module" 且后缀为 .js 时:
require,module.exports 或 exportsimport './src/App.mjs',而非 import './src/App'.cjs,或者 package.json 中 "type": "commonjs" 且后缀为 .js 时:
import 和 exportRspack 原生支持 ES modules 语法,可以使用静态的 import、export 和 import() 语法。
如果使用其他的 ES6+ 特性,你仍然需要引入 SWC 或 Babel 进行转换。
静态 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 var Count = 5;
export function Multiply(a, b) {
return a * b;
}
// 默认导出
export default {
// Some data...
};function import(path: string): Promise;动态加载模块,参考 Dynamic import 了解更多。
对 import() 的调用被视为分割点,这意味着请求的模块及其子模块被拆分成单独的 chunk。
if (module.hot) {
import('lodash').then(_ => {
// 使用 lodash 做些事情...
});
}
此功能内部依赖于 Promise。如果你在旧版浏览器中使用 import(),请记得使用类似于 core-js, es6-promise 或 promise-polyfill 的 polyfill 来模拟 Promise。
import() 无法使用完全动态的导入语句,例如 import(foo)。因为 foo 可能是系统或项目中任何文件的任何路径。
import() 必须至少包含一些关于模块所在位置的信息。可以将打包限制在特定目录或一组文件中,这样当你使用动态表达式时,在 import() 调用中可能被请求的每个模块都会被包括进来。
例如,import(./locale/${language}.json) 会将 ./locale 目录中的每个 .json 文件都被打包进新的代码块。在运行时,一旦变量 language 被计算出来,任何像 english.json 或 german.json 这样的文件都将可供使用。
// 想象一下,如果我们有一种方法可以从 Cookie 或其他存储中获取语言
const language = detectVisitorLanguage();
import(`./locale/${language}.json`).then(module => {
// 做一些翻译相关的事情
});通过向 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}`
);boolean设置为 true 时,Rspack 将跳过对该动态导入的静态分析和打包处理。具体表现为:
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);"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)是非常有用的。number: 预取优先级boolean: false 表示不预取, true 表示预取并且优先级是 0告诉浏览器将来可能需要该资源来进行某些导航跳转,详情请查看预取/预载模块。
number: 预载优先级boolean: false 表示不预载, true 表示预载并且优先级是 0告诉浏览器在当前导航期间可能需要该资源,详情请查看预取/预载模块。
string指定新 chunk 的名称。
"low" | "high" | "auto"为指定的动态导入设置 fetchPriority。也可以通过使用 module.parser.javascript.dynamicImportFetchPriority 选项为所有动态导入设置全局默认值。
Regexp在导入解析时匹配的正则表达式。只有匹配的模块才会被打包。
Regexp在导入解析时匹配的正则表达式。只有匹配的模块不会被打包。
请注意,webpackInclude 和 webpackExclude 选项不会影响前缀。例如:./locale。
string | string[]使 Rspack 在处理该动态 import() 模块时仅打包指定的导出。这样可以降低 chunk 的产物体积。
Rspack 也支持 CommonJS 语法,可以使用 require 和 module.exports 语法。
以同步的方式引入其他模块。
require(dependency: string);以同步的方式获取模块的 ID。此方法会将模块打包进最终的 bundle 中,但不会执行模块代码。返回的模块 ID 主要用于与 require.cache[id] 或 Rspack 内部的 __webpack_require__(id) 一起使用,在大多数应用场景中应避免直接使用此方法。
require.resolve(dependency: string);
模块 ID 的类型可以是 number 或 string,具体取决于 optimization.moduleIds 配置。
多处引用同一模块,最终只会产生一次模块执行和一次导出。所以,会在运行时(runtime)中会保存一份缓存。删除此缓存,则会产生新的模块执行和新的导出。
var d1 = require('dependency');
require('dependency') === d1;
delete require.cache[require.resolve('dependency')];
require('dependency') !== d1;require.context 允许你动态地 require 一组模块。
你可以在代码中使用 require.context,Rspack 将在构建时进行解析并引用匹配的模块。
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');
Rspack 在编译时,会通过静态分析来解析 require.context 的参数,因此参数必须是字面量。
例如,filter 的值不允许传入一个变量,也不允许传入 new RegExp() 生成的值,只能是一个正则表达式字面量。
require.ensure() 基本已经被 import() 取代.
将给定 dependencies 单独打包,并异步加载。当使用 CommonJS 语法的时候,这是唯一动态加载依赖的方式。同时,这个 API 也可以用于运行时,只有在满足某些条件时才会加载依赖。
该特性内部依赖于 Promise。 如果你在旧的浏览器上使用 require.ensure, 最好加入 polyfill es6-promise 或 promise-polyfill。
function requireEnsure(
/**
* callback 执行需要的模块列表
*/
dependencies: String[],
/**
* 当 dependencies 中的模块都加载完毕时,这个函数就会被执行。
* require 函数将作为第一个参数传入该函数。
* 在这个函数中也可以进一步使用 require() 来加载模块。
*/
callback: function(require),
/**
* 当加载依赖失败的时候执行的函数
*/
errorCallback?: function(error),
/**
* 指定由 require.ensure() 生成的 chunk 名称。
* 通过指定相同的 chunkName 来合并多个 require.ensure() 调用的代码,从而只生成一个 bundle,
* 从而减少浏览器加载的次数。
*/
chunkName?: string
): Context;var a = require('normal-dep');
if (module.hot) {
require.ensure(['b'], function (require) {
var c = require('c');
// Do something special...
});
}与 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}`)];Rspack 支持使用 import 和 require 语法导入 Data URI 模块。
import DataURI from 'data:text/javascript,export default 42';require('data:text/javascript,module.exports = 42');除此之外,还支持了 Base64 编码:
const {
number,
fn,
} = require('data:text/javascript;charset=utf-8;base64,ZXhwb3J0IGNvbnN0IG51bWJlciA9IDQyOwpleHBvcnQgZnVuY3Rpb24gZm4oKSB7CiAgcmV0dXJuICJIZWxsbyB3b3JsZCI7Cn0=');Data URI 模块可以被用作虚拟模块(Virtual Modules)的实现方式,如:配合 loader 完成运行时动态加载自定义模块。
Rspack 默认支持这些 MIME 类型 的 data URI 模块:application/json、text/javascript、application/javascript、application/node 和 application/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',
// ...
},
];你可以也使用 Rule.mimetype 来扩展 Data URI 模块的匹配规则,例如为 text/javascript 添加自定义的 loaders:
export default {
module: {
rules: [
{
mimetype: 'text/javascript',
use: [
// ...
],
},
],
},
};