CC 4.0 协议声明

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

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

Externals

外部依赖:该选项提供了「从输出的 bundle 中排除依赖」的方法。相反,所创建的 bundle 依赖于那些存在于用户环境(consumer's environment)中的依赖。此功能通常对库开发人员来说是最有用的,然而也会有各种各样的应用程序用到它。

externals

  • 类型: string | object | function | RegExp | Array<string | object | function | RegExp>

防止将某些 import 的包(package)打包到 bundle 中,而是在运行时(runtime)再去从外部获取这些扩展依赖(external dependencies)。

例如,从 CDN 引入 jQuery,而不是把它打包:

index.html

<script
  src="https://code.jquery.com/jquery-3.1.0.js"
  integrity="sha256-slogkvB1K3VOkzAI8QITxV3VzpOnkeNVsKvtkYLMjfk="
  crossorigin="anonymous"
></script>
rspack.config.js
module.exports = {
  //...
  externals: {
    jquery: 'jquery',
  },
};

这样就剥离了那些不需要改动的依赖模块,换句话,下面展示的代码还可以正常运行:

import $ from 'jquery';

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

上面 rspack.config.jsexternals 下指定的属性名称 jquery 表示 import $ from 'jquery' 中的模块 jquery 应该从打包产物中排除。为了替换这个模块,jQuery 值将用于检索全局 jQuery 变量,因为默认的外部库类型是 var,请参阅 externalsType

虽然我们在上面展示了一个使用外部全局变量的示例,但实际上可以以以下任何形式使用外部变量:全局变量、CommonJS、AMD、ES2015 模块,在 externalsType 中查看更多信息。

字符串

根据 externalsType,这可能是全局变量的名称(参见 'global''this''var ''window')或模块的名称(参见 amdcommonjsmoduleumd)。

如果你只定义一个 external,也可以使用快捷语法:

rspack.config.js
module.exports = {
  //...
  externals: 'jquery',
};

等价于

rspack.config.js
module.exports = {
  //...
  externals: {
    jquery: 'jquery',
  },
};

你可以使用 ${externalsType} ${libraryName} 语法来指定外部库类型。它将覆盖 externalsType 选项中指定的默认外部库类型。

例如,如果外部库是一个 CommonJS 模块,你可以指定

rspack.config.js
module.exports = {
  //...
  externals: {
    jquery: 'commonjs jquery',
  },
};

字符串数组

rspack.config.js
module.exports = {
  //...
  externals: {
    subtract: ['./math', 'subtract'],
  },
};

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

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

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

rspack.config.js
module.exports = {
  //...
  externals: {
    subtract: ['commonjs ./math', 'subtract'],
  },
};

对象

WARNING

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

rspack.config.js
module.exports = {
  externals: {
    // 在 `libraryTarget: 'umd'` 和 `externalsType: 'umd'` 时,需严格遵循以下格式:
    lodash: {
      root: '_', // 指向全局变量
      commonjs: 'lodash',
      commonjs2: 'lodash',
      amd: 'lodash',
    },
  },
};

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

函数

  • 类型:

    • function ({ context, request, contextInfo, getResolve }, callback)
    • function ({ context, request, contextInfo, getResolve }) => promise

如果你想要自定义外部化的行为,可以使用函数。例如使用 webpack-node-externals 排除所有来自 node_modules 目录的模块,并提供了选项来允许列出白名单中的包。

函数接收两个入参:

  • ctx (object): 包含文件的详细信息的对象。
    • ctx.context (string): 包含 import 的文件的目录。
    • ctx.request (string): 被请求的 import 路径。
    • ctx.contextInfo (object): 包含有关发起者的信息(例如 layer 和编译器)。
    • ctx.getResolve: 获取一个带有当前解析器选项的解析函数。
  • callback (function (err, result, type)): 用来指示模块如何被外部化的回调函数。

回调函数接收三个参数:

  • err (Error): 用于标识外部化导入时是否出现错误。如果有错误,这应该是唯一使用的参数。
  • result (string | string[] | object): 使用其他外部格式描述外部模块(字符串字符串数组对象 )。
  • type (string): 指示 externalsType 的可选参数(如果还没有在 result 参数中指出的话)。

例如,要外部化所有导入路径与正则表达式匹配的导入,可以这样做:

rspack.config.js
module.exports = {
  //...
  externals: [
    function ({ context, request }, callback) {
      if (/^yourregex$/.test(request)) {
        // 将请求路径外部化到 CommonJS 模块
        return callback(null, 'commonjs ' + request);
      }

      // 跳过外部化导入
      callback();
    },
  ],
};

其他使用不同模块格式的例子:

rspack.config.js
module.exports = {
  externals: [
    function (ctx, callback) {
      // 外部化为 `@scope/library` 的 `commonjs2` 模块
      callback(null, '@scope/library', 'commonjs2');
    },
  ],
};
rspack.config.js
module.exports = {
  externals: [
    function (ctx, callback) {
      // 外部化名为 `nameOfGlobal` 的全局变量
      callback(null, 'nameOfGlobal');
    },
  ],
};
rspack.config.js
module.exports = {
  externals: [
    function (ctx, callback) {
      // 外部化为 `@scope/library` 模块中的具名导出
      callback(null, ['@scope/library', 'namedexport'], 'commonjs');
    },
  ],
};
rspack.config.js
module.exports = {
  externals: [
    function (ctx, callback) {
      // 外部化为 UMD 模块
      callback(null, {
        root: 'componentsGlobal',
        commonjs: '@scope/components',
        commonjs2: '@scope/components',
        amd: 'components',
      });
    },
  ],
};

正则表达式

符合给定正则表达式的每个依赖项都将从打包产物中排除。

rspack.config.js
module.exports = {
  //...
  externals: /^(jquery|\$)$/i,
};

在这种情况下,任何名为 jQuery 的依赖项,无论是否大写,或者 $,都将被外部化。

复合语法

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

rspack.config.js
module.exports = {
  //...
  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
  • 默认值: 'var'

指定 externals 的默认类型。当 external 被设置为 amdumdsystem 以及 jsonp 时,output.libraryTarget 的值也应相同。例如,你只能在 amd 库中使用 amd 的 externals。

支持的类型如下:

rspack.config.js
module.exports = {
  //...
  externalsType: 'promise',
};

externalsType.commonjs

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

示例

import fs from 'fs-extra';
rspack.config.js
module.exports = {
  // ...
  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.js
module.exports = {
  // ...
  externalsType: 'global',
  externals: {
    jquery: '$',
  },
  output: {
    globalObject: 'global',
  },
};

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

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

externalsType.module

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

确保开启了 experiments.outputModule,否则 Rspack 会抛出错误。

示例

import jq from 'jquery';
jq('.my-element').animate(/* ... */);
rspack.config.js
module.exports = {
  experiments: {
    outputModule: true,
  },
  externalsType: 'module',
  externals: {
    jquery: 'jquery',
  },
};

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

import * as __WEBPACK_EXTERNAL_MODULE_jquery__ from 'jquery';

const jq = __WEBPACK_EXTERNAL_MODULE_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.js
module.exports = {
  externalsType: 'import',
  externals: {
    jquery: 'jquery',
  },
};

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

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

// webpack 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.js
module.exports = {
  externalsType: 'module-import',
  externals: {
    jquery: 'jquery',
  },
};

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

import * as __WEBPACK_EXTERNAL_MODULE_lodash__ from 'lodash';
const lodash = __WEBPACK_EXTERNAL_MODULE_jquery__;

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

// webpack runtime...

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

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

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

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

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.js
module.exports = {
  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');
  },
};

// webpack runtime...

async function foo() {
  const jq = await Promise.resolve(/* import() */).then(
    __webpack_require__.bind(__webpack_require__, 'jquery'),
  );
  (0, lodash__WEBPACK_IMPORTED_MODULE_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.js
module.export = {
  externalsType: 'node-commonjs',
  externals: {
    jquery: 'jquery',
  },
};

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

import { createRequire } from '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.js
module.exports = {
  // ...
  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.js
module.exports = {
  // ...
  externalsType: 'self',
  externals: {
    jquery: '$',
  },
};

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

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

externalsType.script

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

语法

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

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

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

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

示例

让我们从 CDN 加载一个 lodash

rspack.config.js
module.exports = {
  // ...
  externalsType: 'script',
  externals: {
    lodash: ['https://cdn.jsdelivr.net/npm/lodash@4.17.19/lodash.min.js', '_'],
  },
};

然后在代码中使用:

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

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

rspack.config.js
module.exports = {
  // ...
  externalsType: 'script',
  externals: {
    lodash: [
      'https://cdn.jsdelivr.net/npm/lodash@4.17.19/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-webpack 属性的现有 <script> 标签。对于 chunk 加载,data-webpack 属性的值将是 '[output.uniqueName]:chunk-[chunkId]',而外部脚本的值将是 '[output.uniqueName]:[global]'

externalsType.this

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

示例

import jq from 'jquery';
jq('.my-element').animate(/* ... */);
rspack.config.js
module.exports = {
  // ...
  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.js
module.exports = {
  // ...
  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.js
module.exports = {
  // ...
  externalsType: 'window',
  externals: {
    jquery: '$',
  },
};

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

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

externalsPresets

  • 类型: object

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

externalsPresets.electron

类型:boolean

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

externalsPresets.electronMain

类型:boolean

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

externalsPresets.electronPreload

类型:boolean

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

externalsPresets.electronRenderer

类型:boolean

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

externalsPresets.node

类型:boolean

将 node.js 的内置模块(如 fspathvm)视为外部模块,并在使用时通过 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.js
module.exports = {
  // ...
  externalsPresets: {
    node: true,
  },
};