For AI agents: the complete documentation index is available at /zh/llms.txt, the full documentation bundle is available at /zh/llms-full.txt, and this page is available as Markdown at /zh/config/externals.md.
close

Externals

外部依赖(externals)用于指定哪些模块不需要被 Rspack 打包,而是直接使用外部环境中提供的实现。

例如,当页面已经通过 CDN 引入了 React,或你开发的库希望由使用方自行安装 react 时,可以将其声明为 external。这可以减少打包产物的体积,同时避免重复引入相同依赖。

该能力常用于库开发场景,同时在应用侧接入 CDN、使用宿主环境注入依赖等场景中也同样适用。

  • 类型:
type ExternalItem =
  string | RegExp | Function | { [x: string]: ExternalItemValue };

type Externals = ExternalItem | ExternalItem[];

基本用法

例如,当页面已通过 CDN 引入 Day.js 包时,可以将其声明为 external,避免被重复打包:

index.html
<script src="https://cdn.jsdelivr.net/npm/dayjs@1/dayjs.min.js"></script>
rspack.config.mjs
export default {
  externals: {
    dayjs: 'dayjs',
  },
};

此时,dayjs 模块会从打包产物中剥离,转而在运行时从外部环境中获取,因此下面的代码仍然可以正常运行:

import dayjs from 'dayjs';

console.log(dayjs().format('YYYY-MM-DD'));

在上述配置中,externals 的键名 dayjs 对应 import dayjs from 'dayjs' 中的模块标识符,表示该模块不会被打包。

对应的值 dayjs 则用于在运行时访问全局变量。在这个基础配置中,externalsType 的默认值为 var,即从全局作用域中读取该变量。在浏览器环境下,这通常等价于访问 window.dayjs。

字符串

在前面的示例中,externals 的值使用了字符串形式,该字符串的具体含义取决于 externalsType 配置。

通常情况下,这个字符串可以表示:

简写语法

当只需要声明一个 external 时,也可以使用更简洁的写法:

rspack.config.mjs
export default {
  externals: 'lodash', // 等价于 externals: { lodash: 'lodash' }
};

匹配规则

需要注意的是,字符串形式是精确匹配。

例如,下面的配置只会匹配 react-dom,不会匹配 react-dom/client 这样的子路径导入:

rspack.config.mjs
export default {
  externals: {
    'react-dom': 'react-dom',
  },
  externalsType: 'module-import',
};

如果你也希望这些子路径导入同样被 external,可以把它们一并列出来:

rspack.config.mjs
export default {
  externals: {
    'react-dom': 'react-dom',
    'react-dom/client': 'react-dom/client',
  },
  externalsType: 'module-import',
};

如果需要匹配一组相似的导入形式,也可以改用正则表达式。

指定外部模块类型

此外,还可以通过 ${externalsType} ${libraryName} 的语法显式指定外部模块类型,这会覆盖 externalsType 的默认值。

例如,当外部依赖以 ES module 形式提供时,可以这样配置:

rspack.config.mjs
export default {
  externals: {
    react: 'module-import react',
  },
};

字符串数组

rspack.config.mjs
export default {
  externals: {
    subtract: ['./math', 'subtract'],
  },
};

subtract: ['./math', 'subtract'] 允许你选择一个模块的一部分,其中 ./math 是模块名,你的打包只需要包含 subtract 变量下的子集。

当 externalsType 为 commonjs 时,这个例子会编译为 require('./math').subtract;当 externalsType 为 window 时,这个例子会编译为 window["./math"]["subtract"]。

与字符串语法类似,你可以在数组的第一个元素中使用 ${externalsType} ${libraryName} 语法指定外部库类型,例如:

rspack.config.mjs
export default {
  externals: {
    subtract: ['commonjs ./math', 'subtract'],
  },
};

对象

Warning

带有 { root, commonjs, commonjs2, amd, ... } 的对象只允许用于 library.type: 'umd' 和 externalsType: 'umd'。其他库的 target 不允许这样做。

rspack.config.mjs
export default {
  externals: {
    // 在 `library.type: 'umd'` 和 `externalsType: 'umd'` 时,需严格遵循以下格式:
    lodash: {
      root: '_', // 指向全局变量
      commonjs: 'lodash',
      commonjs2: 'lodash',
      amd: 'lodash',
    },
  },
};

此语法用于描述外部 library 所有可用的访问方式。这里 lodash 这个外部库可以在 AMD 和 CommonJS 模块系统中通过 lodash 访问,但在全局变量形式下用 _ 访问。subtract 可以通过全局 math 对象下的属性 subtract 访问(例如 window['math']['subtract'])。

函数

类型:

type ExternalFunction = (
  data: ExternalItemFunctionData,
  callback: (
    error?: Error | null,
    result?: ExternalItemValue,
    externalsType?: ExternalsType,
  ) => void,
) => ExternalItemValue | void | Promise<ExternalItemValue | void>;

如果需要根据模块请求或其他模块信息决定是否将依赖外部化,可以使用函数。函数可以直接返回结果、返回 Promise,或调用回调。

例如,下面的配置将 react 及其子路径保留为 ES 模块导入:

rspack.config.mjs
export default {
  output: {
    module: true,
  },
  externalsType: 'module',
  externals: ({ request }) => {
    if (request === 'react' || request?.startsWith('react/')) {
      return request;
    }
  },
};

使用产物的环境需要能够解析这些导入,例如通过另一个打包工具或浏览器的 import map。

同步返回结果时,函数应只声明一个参数,例如 ({ request }) => ...。Rspack 通过函数的参数个数区分同步返回和回调用法。需要异步处理时,可以使用 async 函数或返回 Promise。

请求信息

第一个参数包含依赖的相关信息:

  • request:源码中的模块请求,例如 react 或 ./utils.js,此时尚未解析为文件路径。
  • context:解析请求时使用的起始目录。
  • dependencyType:依赖类别,例如 esm 或 commonjs。
  • contextInfo.issuer:发起请求的模块路径。
  • contextInfo.issuerLayer:发起请求的模块所属的层(如果已设置)。
  • getResolve:使用当前解析选项创建解析函数,详见解析请求。

返回结果

直接返回的值、Promise 的结果和回调的结果具有相同含义:

  • 字符串、字符串数组或对象:按对应的值格式将请求外部化,详见字符串、字符串数组和对象。
  • true:使用原始请求名称和 externalsType 将请求外部化。
  • false:停止检查当前 externals 配置的剩余规则,不将请求外部化。其他 ExternalsPlugin 实例仍可能将其外部化。
  • undefined:继续检查下一条规则。使用回调时,调用 callback()。

如果没有规则将请求外部化,Rspack 会继续正常的模块处理流程。若要为单个结果覆盖 externalsType,可以添加 'window _' 这样的类型前缀,或通过回调的第三个参数指定类型。

回调用法

第二个参数是回调函数。失败时调用 callback(error),提供结果时调用 callback(null, result, externalsType?),继续检查其他规则则调用 callback()。使用这种形式时,每个分支都应调用回调,而不是直接返回结果。

上面的 ESM 示例也可以改用回调:

rspack.config.mjs
export default {
  output: {
    module: true,
  },
  externalsType: 'module',
  externals: ({ request }, callback) => {
    if (request === 'react' || request?.startsWith('react/')) {
      callback(null, request);
      return;
    }
    callback();
  },
};

解析请求

如果需要根据解析后的路径或包元数据决定是否将依赖外部化,可以使用 getResolve()。例如,在接收 { context, request, getResolve } 的异步外部依赖函数中:

const resolve = getResolve();
const resolvedPath = await resolve(context, request);

可以向 getResolve(options) 传入解析选项,定制解析函数。返回的函数也支持回调:resolve(context, request, (error, resolvedPath, resolveData) => { ... })。

解析成功时会返回路径。如果请求被忽略,例如其别名被设为 false,Promise 的结果为 undefined,回调的第二个参数为 false。解析失败时,Promise 会被拒绝,或通过回调的第一个参数传递错误。

使用回调时,可选的第三个参数包含解析详情。其中的 descriptionFileData 字段在有值时是解析后的 package.json 对象,可以直接读取 name、version、type 等字段,无需调用 JSON.parse()。

正则表达式

你也可以使用正则表达式来匹配需要外部化的模块。所有符合该正则的模块标识符,都会从打包产物中排除。

rspack.config.mjs
export default {
  externals: /react-dom/i,
};

在上述配置中,所有匹配 react-dom 的模块标识符都会被 external,例如 react-dom、react-dom/client 等。

复合语法

有时你可能希望使用上述语法的组合。这可以通过以下方式完成:

rspack.config.mjs
export default {
  externals: [
    {
      // 字符串
      react: 'react',
      // 对象
      lodash: {
        commonjs: 'lodash',
        amd: 'lodash',
        root: '_', // indicates global variable
      },
      // 字符串数组
      subtract: ['./math', 'subtract'],
    },
    // 函数
    function ({ context, request }, callback) {
      if (/^yourregex$/.test(request)) {
        return callback(null, 'commonjs ' + request);
      }
      callback();
    },
    // 正则表达式
    /^(jquery|\$)$/i,
  ],
};
Warning

如果没有指定类型,将使用默认类型,例如 externals: { react: 'react' } 而不是 externals: { react: 'commonjs-module react' }。

externalsType

  • 类型: string

externalsType 决定 Rspack 默认如何加载 external 依赖。使用 'amd'、'umd'、'system' 或 'jsonp' 类型时,output.library.type 必须设置为相同的值。例如,只有 'amd' 库才能使用 'amd' 类型的 external。

如果 external 依赖需要使用与推导结果不同的加载格式,请显式设置 externalsType。

默认值

默认值按以下顺序推导:

出于向后兼容的考虑,output.library.type: 'modern-module' 目前会跳过第一条规则,继续按照 output.module 推导默认值。如需使用 modern module externals,请显式设置 externalsType: 'modern-module'。

可选值

支持的类型如下:

rspack.config.mjs
export default {
  externalsType: 'promise',
};

externalsType.commonjs

将 externals 的默认类型指定为 'commonjs'。Rspack 将为模块中使用的外部生成类似 const X = require('...') 的代码。

示例

import fs from 'fs-extra';
rspack.config.mjs
export default {
  externalsType: 'commonjs',
  externals: {
    'fs-extra': 'fs-extra',
  },
};

将会转换为类似下面的代码:

const fs = require('fs-extra');

请注意,输出产物中会有一个 require()。

externalsType.global

将 externals 的默认类型指定为 'global'。Rspack 会将 external 视为 globalObject 上的一个全局变量读取。

示例

import jq from 'jquery';
jq('.my-element').animate(/* ... */);
rspack.config.mjs
export default {
  externalsType: 'global',
  externals: {
    jquery: '$',
  },
  output: {
    globalObject: 'global',
  },
};

将会转换为类似下面的代码:

const jq = global['$'];
jq('.my-element').animate(/* ... */);

externalsType.module

将 externals 的默认类型指定为 'module'。Rspack 将为模块中使用的 externals 生成类似 import * as X from '...' 的代码。

确保开启了 output.module。

示例

import jq from 'jquery';
jq('.my-element').animate(/* ... */);
rspack.config.mjs
export default {
  output: {
    module: true,
  },
  externalsType: 'module',
  externals: {
    jquery: 'jquery',
  },
};

将会转换为类似下面的代码:

import * as __rspack_external_jquery from 'jquery';

const jq = __rspack_external_jquery['default'];
jq('.my-element').animate(/* ... */);

请注意,在输出产物中将有 import 语句。

externalsType.import

将 externals 的默认类型指定为 'import'。Rspack 将为模块中使用的 externals 生成类似 import('...') 的代码。

示例

async function foo() {
  const jq = await import('jquery');
  jq('.my-element').animate(/* ... */);
}
rspack.config.mjs
export default {
  externalsType: 'import',
  externals: {
    jquery: 'jquery',
  },
};

将会转换为类似下面的代码:

var __webpack_modules__ = {
  jquery: (module) => {
    module.exports = import('jquery');
  },
};

// Rspack runtime...

async function foo() {
  const jq = await Promise.resolve(/* import() */).then(
    __webpack_require__.bind(__webpack_require__, 'jquery'),
  );
  jq('.my-element').animate(/* ... */);
}

请注意,在输出产物中将有 import() 语句。

externalsType['module-import']

将 externals 的默认类型指定为 'module-import'。这将结合 'module' 和 'import'。Rspack 将自动检测导入语法的类型,对于静态导入设置为 'module',对于动态导入设置为 'import'。

示例

import { attempt } from 'lodash';

async function foo() {
  const jq = await import('jquery');
  attempt(() => jq('.my-element').animate(/* ... */));
}
rspack.config.mjs
export default {
  externalsType: 'module-import',
  externals: {
    jquery: 'jquery',
  },
};

将会转换为类似下面的代码:

import * as __rspack_external_lodash from 'lodash';
const lodash = __rspack_external_jquery;

var __webpack_modules__ = {
  jquery: (module) => {
    module.exports = import('jquery');
  },
};

// Rspack runtime...

async function foo() {
  const jq = await Promise.resolve(/* import() */).then(
    __webpack_require__.bind(__webpack_require__, 'jquery'),
  );
  (0, lodash.attempt)(() => jq('.my-element').animate(/* ... */));
}

请注意,在输出产物中将有 import 或 import() 语句。

当一个模块没有通过 import 或 import() 导入时,Rspack 将使用 "module" externals type 作为回退。如果你想使用不同类型的 externals 作为回退,你可以在 externals 选项中指定一个函数。例如:

rspack.config.mjs
export default {
  externalsType: 'module-import',
  externals: [
    function ({ request, dependencyType }, callback) {
      if (dependencyType === 'commonjs') {
        return callback(null, `node-commonjs ${request}`);
      }
      callback();
    },
  ],
};

externalsType['modern-module']

将 externals 的默认类型指定为 'modern-module'。

出于兼容性考虑,Rspack 暂时不会在 output.library.type 为 'modern-module' 时自动启用它。请显式配置 externalsType: 'modern-module' 来开启。下一个 major 版本中,它会成为 modern-module library 的默认值。

对于静态 ESM import,Rspack 会像 'module' 一样渲染 external,生成静态 import 语句。对于动态 import(),Rspack 会像 'module-import' 一样渲染 external,生成 import()。

对于 CommonJS require(),Rspack 会根据 target 选择 CommonJS external 变体。Node-like target 会像 'node-commonjs' 一样渲染 external,在生成的 ESM 产物中通过 createRequire 构造 require 函数。其他 target 会像 'commonjs' 一样渲染 external,保留裸 require() 调用。

示例

src/index.js
import { readFile } from 'node:fs/promises';

const path = require('node:path');

export async function loadConfig(file) {
  const os = await import('node:os');
  const content = await readFile(file, 'utf-8');
  return {
    content,
    dirname: path.dirname(file),
    platform: os.platform(),
  };
}
rspack.config.mjs
export default {
  target: 'node',
  output: {
    library: {
      type: 'modern-module',
    },
  },
  externalsType: 'modern-module',
  externals: ['node:fs/promises', 'node:path', 'node:os'],
};

因为这个示例的 target 是 Node.js,生成的 ESM 产物会保留静态 import 为 import 语句,保留动态 import 为 import(),并通过 createRequire 渲染 require() external:

import { createRequire as __rspack_createRequire } from 'node:module';
import { readFile } from 'node:fs/promises';

const __rspack_createRequire_require = __rspack_createRequire(import.meta.url);
const path = __rspack_createRequire_require('node:path');

async function loadConfig(file) {
  const os = await import('node:os');
  // ...
}

export { loadConfig };

externalsType['commonjs-import']

将 externals 的默认类型指定为 'commonjs-import'。这将结合 'commonjs' 和 'import'。Rspack 将自动检测导入语法的类型,对于动态导入设置为 'import',其他的导入设置为 'commonjs'。

这在构建一个 Node.js 应用程序时非常有用,当目标 Node.js 版本高于 13.2.0,同时支持 import() 表达式 和 require()。

Note

commonjs-import 类型仅在 Rspack 中可用,webpack 并不支持此类型。

示例

import { attempt } from 'lodash';

async function foo() {
  const jq = await import('jquery');
  attempt(() => jq('.my-element').animate(/* ... */));
}
rspack.config.mjs
export default {
  externalsType: 'commonjs-import',
  externals: {
    lodash: 'lodash',
    jquery: 'jquery',
  },
};

将会转换为类似下面的代码:

var __webpack_modules__ = {
  lodash: function (module) {
    module.exports = require('lodash');
  },
  jquery: function (module) {
    module.exports = import('jquery');
  },
};

// Rspack runtime...

async function foo() {
  const jq = await Promise.resolve(/* import() */).then(
    __webpack_require__.bind(__webpack_require__, 'jquery'),
  );
  (0, lodash__rspack_import_0__.attempt)(() =>
    jq('.my-element').animate(/* ... */),
  );
}

请注意,在输出产物中将有 import() 语句。

externalsType['node-commonjs']

将 externals 类型设置为 'node-commonjs',Rspack 将从 module 中导入 createRequire 来构造一个 require 函数,用于加载模块中使用的外部对象。

示例

import jq from 'jquery';
jq('.my-element').animate(/* ... */);
rspack.config.mjs
export default {
  externalsType: 'node-commonjs',
  externals: {
    jquery: 'jquery',
  },
};

将会转换为类似下面的代码:

import { createRequire } from 'node:module';

const jq = createRequire(import.meta.url)('jquery');
jq('.my-element').animate(/* ... */);

请注意,在输出产物中会有 import 语句。

externalsType.promise

将 externals 的默认类型指定为 'promise',Rspack 会将 external 视为全局变量(类似于 'var')并 await 它。

例子

import jq from 'jquery';
jq('.my-element').animate(/* ... */);
rspack.config.mjs
export default {
  externalsType: 'promise',
  externals: {
    jquery: '$',
  },
};

将会转换为类似下面的代码:

const jq = await $;
jq('.my-element').animate(/* ... */);

externalsType.self

将外部的默认类型指定为 'self'。 Rspack 会将 external 作为 self 对象上的全局变量读取。

import jq from 'jquery';
jq('.my-element').animate(/* ... */);
rspack.config.mjs
export default {
  externalsType: 'self',
  externals: {
    jquery: '$',
  },
};

将会转换为类似下面的代码:

const jq = self['$'];
jq('.my-element').animate(/* ... */);

externalsType.script

将 external 的默认类型指定为 'script'。Rspack 会使用 HTML <script> 标签加载外部资源,暴露预定义的全局变量。脚本加载完成后,<script> 标签将被移除。

语法

rspack.config.mjs
export default {
  externalsType: 'script',
  externals: {
    packageName: [
      'http://example.com/script.js',
      'global',
      'property',
      'property',
    ], // 属性是可选的
  },
};

如果不打算指定任何属性也可以使用快捷语法:

rspack.config.mjs
export default {
  externalsType: 'script',
  externals: {
    packageName: 'global@http://example.com/script.js', // 这里没有属性
  },
};

请注意,output.publicPath 不会被添加到提供的 URL 中。

示例

让我们从 CDN 加载一个 lodash:

rspack.config.mjs
export default {
  externalsType: 'script',
  externals: {
    lodash: ['https://cdn.jsdelivr.net/npm/[email protected]/lodash.min.js', '_'],
  },
};

然后在代码中使用:

import _ from 'lodash';
console.log(_.head([1, 2, 3]));

下面是我们如何为上述示例指定属性的方法:

rspack.config.mjs
export default {
  externalsType: 'script',
  externals: {
    lodash: [
      'https://cdn.jsdelivr.net/npm/[email protected]/lodash.min.js',
      '_',
      'head',
    ],
  },
};

局部变量 head 和全局 window._ 在导入 lodash 时都将被暴露出来。

import head from 'lodash';
console.log(head([1, 2, 3])); // logs 1 here
console.log(window._.head(['a', 'b'])); // logs a here
Tip

当使用 HTML <script> 标签加载代码时,Rspack 运行时会尝试查找与 src 属性匹配或具有特定 data-rspack 属性的现有 <script> 标签。对于 chunk 加载,data-rspack 属性的值将是 '[output.uniqueName]:chunk-[chunkId]',而外部脚本的值将是 '[output.uniqueName]:[global]'。

externalsType.this

将 external 的默认类型指定为 'this'。Rspack 会将 external 作为 this 对象上的全局变量读取。

示例

import jq from 'jquery';
jq('.my-element').animate(/* ... */);
rspack.config.mjs
export default {
  externalsType: 'this',
  externals: {
    jquery: '$',
  },
};

将会转换为类似下面的代码:

const jq = this['$'];
jq('.my-element').animate(/* ... */);

externalsType.var

将 external 的默认类型指定为 'var'。Rspack 会将 external 作为全局变量读取。

示例

import jq from 'jquery';
jq('.my-element').animate(/* ... */);
rspack.config.mjs
export default {
  externalsType: 'var',
  externals: {
    jquery: '$',
  },
};

将会转换为类似下面的代码:

const jq = $;
jq('.my-element').animate(/* ... */);

externalsType.window

将 external 的默认类型指定为 'window'。Rspack 会将 external 作为 window 对象上的全局变量读取。

示例

import jq from 'jquery';
jq('.my-element').animate(/* ... */);
rspack.config.mjs
export default {
  externalsType: 'window',
  externals: {
    jquery: '$',
  },
};

将会转换为类似下面的代码:

const jq = window['$'];
jq('.my-element').animate(/* ... */);

externalsPresets

  • 类型: object

为特定的目标环境启用外部模块的预设值。

externalsPresets.electron

类型:boolean

将 Electron 主进程和预加载脚本中常见的 Electron 内置模块如 electron、ipc 或 shell 视为外部模块,并在使用时通过 require() 加载它们。

externalsPresets.electronMain

类型:boolean

将 Electron 主进程中常见的 Electron 内置模块如 app、ipc-main 或 shell 视为外部模块,并在使用时通过 require() 加载它们。

externalsPresets.electronPreload

类型:boolean

将 Electron 预加载脚本中常见的 Electron 内置模块如 web-frame、ipc-renderer 或 shell 视为外部模块,并在使用时通过 require() 加载它们。

externalsPresets.electronRenderer

类型:boolean

将 Electron 渲染进程中常见的 Electron 内置模块如 web-frame、ipc-renderer 或 shell 视为外部模块,并在使用时通过 require() 加载它们。

externalsPresets.node

类型:boolean

将 node.js 的内置模块(如 fs、path 或 vm)视为外部模块,并在使用时通过 require() 加载它们。

externalsPresets.nwjs

类型:boolean

将 NW.js 旧版 nw.gui 模块视为外部模块,并在使用时通过 require() 加载它。

externalsPresets.web

类型:boolean

将对 http(s)://... 和 std:... 引入的模块视为外部模块,并在使用时通过 import (externalType: "module") 来加载它们 (请注意,这会改变执行顺序,因为外部模块的代码会在 chunk 中的任何其他模块的代码之前执行)。

externalsPresets.webAsync

类型:boolean

将对 http(s)://... 和 std:... 引入的模块视为外部模块,并在使用时通过 async import() 来加载它们 (注意,外部模块的类型是 async 模块,对执行有各种影响)。

请注意,如果你打算输出 ES 模块并使用这些 node.js 相关的预设值,Rspack 将会将默认的 externalsType 设置为 node-commonjs,这将使用 createRequire 来构造一个 require 函数,而不是使用 require()。

示例

使用 node 预设将不会打包内置模块,并将它们视为外部模块,当使用时通过 require() 加载它们。

rspack.config.mjs
export default {
  externalsPresets: {
    node: true,
  },
};

本页改编自 webpack 文档,遵循 CC BY 4.0,且已作修改。