知海

Externals 配置

webpackjsorg-main配置参考

Externals 配置

externals 配置选项提供了一种从输出 bundle 中排除依赖项的方法。相反,创建的 bundle 会依赖该依赖项在消费者(任何最终用户应用程序)的环境中存在。此功能通常对库开发人员最有用,但也有多种应用场景。

externals

string object function RegExp [string, object, function, RegExp]

阻止打包某些 import 的包,并在运行时检索这些 外部依赖项

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

index.html

html 复制代码
<script
  src="https://code.jquery.com/jquery-3.1.0.js"
  integrity="sha256-slogkvB1K3VOkzAI8QITxV3VzpOnkeNVsKvtkYLMjfk="
  crossorigin="anonymous"
></script>

webpack.config.js

js 复制代码
export default {
  // ...
  externals: {
    jquery: "jQuery",
  },
};

这样不会改动任何依赖模块,即下面的代码仍然可以正常工作:

js 复制代码
import $ from "jquery";

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

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

虽然我们上面演示了如何使用外部全局变量,但外部依赖实际上可以是以下任意一种形式:全局变量、CommonJS、AMD、ES2015 模块,详见 externalsType

string

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

如果你只定义一个外部依赖,也可以使用简写语法:

js 复制代码
export default {
  // ...
  externals: "jquery",
};

等同于

js 复制代码
export default {
  // ...
  externals: {
    jquery: "jquery",
  },
};

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

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

js 复制代码
export default {
  // ...
  externals: {
    jquery: "commonjs jquery",
  },
};

[string]

js 复制代码
export default {
  // ...
  externals: {
    subtract: ["./math", "subtract"],
  },
};

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

externalsTypecommonjs 时,此示例将转换为 require('./math').subtract; 而当 externalsTypewindow 时,此示例将转换为 window["./math"]["subtract"];

string 语法 类似,你可以在数组的第一项中使用 ${externalsType} ${libraryName} 语法来指定外部库类型,例如:

js 复制代码
export default {
  // ...
  externals: {
    subtract: ["commonjs ./math", "subtract"],
  },
};

object

警告: 具有 { root, amd, commonjs, ... } 的对象仅允许用于 libraryTarget: 'umd'externalsType: 'umd'。它不允许用于其他库目标。

警告: 自 webpack 5.109.0 起,对于所使用的 externals 类型,如果没有对应的条目,对象形式的外部依赖会在构建时报错,而不是在运行时静默解析为 undefined

js 复制代码
export default {
  // ...
  // 或
  externals: {
    react: "react",
  },
};
js 复制代码
export default {
  // ...
  // 或
  externals: {
    lodash: {
      commonjs: "lodash",
      amd: "lodash",
      root: "_", // 表示全局变量
    },
  },
};
js 复制代码
export default {
  // ...
  // 或
  externals: {
    subtract: {
      root: ["math", "subtract"],
    },
  },
};

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

interop

5.109.0+

非 ESM 外部依赖(commonjsamdumd 等)是动态模块:在严格的 ES 模块(带有 "type": "module" 的包)中导入它们的 default 会得到整个导出对象,而非严格导入则会通过运行时的 __esModule 检查将其解包。对象形式外部依赖上的可选 interop 提示可以固定此行为,而不受导入方的影响,类似于 Rollup 的 output.interop

  • 'esModule' - 将外部依赖视为 ES 模块命名空间,因此 default 导入会解析为其 .default 导出。
  • 'default' - 将外部依赖视为 CommonJS 模块,因此 default 导入会解析为整个导出对象(Node.js 语义)。
js 复制代码
export default {
  // ...
  externals: {
    dep: {
      amd: "dep",
      interop: "esModule",
    },
  },
};

function

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

定义你自己的函数来控制 webpack 要外部化的行为可能很有用。例如,webpack-node-externals 会排除 node_modules 目录中的所有模块,并提供选项来白名单包。

该函数可以接收以下参数:

  • ctxobject):包含文件详细信息的对象。
    • ctx.contextstring):包含导入的文件的目录。
    • ctx.requeststring):被请求的导入路径。
    • ctx.contextInfoobject):包含有关发起者的信息(例如 layer 和 compiler)。
    • ctx.getResolve 5.15.0+:使用当前解析器选项获取一个 resolve 函数。
  • callbackfunction (err, result, type)):用于指示模块应如何被外部化的回调函数。

回调函数接收三个参数:

  • errError):用于指示外部化导入时是否出错。如果出错,这应该是唯一使用的参数。
  • resultstring [string] object):使用其他外部格式(string[string]object)描述外部模块。
  • typestring):可选参数,指示模块的 外部类型(如果尚未在 result 参数中指明)。

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

webpack.config.js

js 复制代码
export default {
  // ...
  externals: [
    function ({ context, request }, callback) {
      if (/^yourregex$/.test(request)) {
        // 外部化为使用请求路径的 commonjs 模块
        return callback(null, `commonjs ${request}`);
      }

      // 继续而不外部化导入
      callback();
    },
  ],
};

使用其他模块格式的其他示例:

webpack.config.js

js 复制代码
export default {
  externals: [
    function (ctx, callback) {
      // 外部化是位于 `@scope/library` 中的 `commonjs2` 模块
      callback(null, "@scope/library", "commonjs2");
    },
  ],
};

webpack.config.js

js 复制代码
export default {
  externals: [
    function (ctx, callback) {
      // 外部化是名为 `nameOfGlobal` 的全局变量
      callback(null, "nameOfGlobal");
    },
  ],
};

webpack.config.js

js 复制代码
export default {
  externals: [
    function (ctx, callback) {
      // 外部化是 `@scope/library` 模块中的命名导出
      callback(null, ["@scope/library", "namedexport"], "commonjs");
    },
  ],
};

webpack.config.js

js 复制代码
export default {
  externals: [
    function (ctx, callback) {
      // 外部化是一个 UMD 模块
      callback(null, {
        root: "componentsGlobal",
        commonjs: "@scope/components",
        commonjs2: "@scope/components",
        amd: "components",
      });
    },
  ],
};

RegExp

与给定正则表达式匹配的每个依赖项都将从输出 bundle 中排除。

webpack.config.js

js 复制代码
export default {
  // ...
  externals: /^(jquery|\$)$/i,
};

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

组合语法

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

webpack.config.js

js 复制代码
export default {
  // ...
  externals: [
    {
      // 字符串
      react: "react",
      // 对象
      lodash: {
        commonjs: "lodash",
        amd: "lodash",
        root: "_", // 表示全局变量
      },
      // [string]
      subtract: ["./math", "subtract"],
    },
    // 函数
    function ({ context, request }, callback) {
      if (/^yourregex$/.test(request)) {
        return callback(null, `commonjs ${request}`);
      }
      callback();
    },
    // 正则
    /^(jquery|\$)$/i,
  ],
};

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

有关如何使用此配置的更多信息,请参阅有关 如何编写一个库 的文章。

byLayer

function object

按 layer 指定外部依赖。

webpack.config.js

js 复制代码
export default {
  externals: {
    byLayer: {
      layer: {
        external1: "var 43",
      },
    },
  },
};

externalsType

string = 'var'

指定默认的外部依赖类型。amdumdsystemjsonp 外部依赖依赖于 output.libraryTarget 被设置为相同的值,例如你只能在 amd 库中使用 amd 外部依赖。

支持的类型:

webpack.config.js

js 复制代码
export default {
  // ...
  externalsType: "promise",
};

externalsType.amd-async

5.109.0+

将 externals 的默认类型设置为 'amd-async'。与 'amd' 一样,外部依赖通过 AMD 加载器解析,但它在运行时通过异步 require([...]) API 加载,并作为异步模块公开。这意味着输出 bundle 本身不需要包装在 AMD 库中(不需要匹配的 output.library.type),因此可以从任何 chunk 格式使用仅 AMD 的外部依赖。

示例

js 复制代码
import _ from "lodash";

webpack.config.js

js 复制代码
export default {
  // ...
  externalsType: "amd-async",
  externals: {
    lodash: "lodash",
  },
};

外部模块解析为类似下面的表达式,并通过 webpack 的异步模块运行时公开:

js 复制代码
new Promise((resolve, reject) => {
  if (typeof require !== "function") {
    reject(
      new Error(
        "AMD 'require' is not available to load external module lodash",
      ),
    );
    return;
  }
  require(["lodash"], (module) => resolve(module), reject);
});

提示: 当设置了 output.library.amdContainer 时,异步 require 调用将针对该容器对象发出,而不是全局 AMD require

externalsType.commonjs

将 externals 的默认类型设置为 'commonjs'。webpack 将为模块中使用的外部依赖生成类似 const X = require('...') 的代码。

示例

js 复制代码
import fs from "fs-extra";

webpack.config.js

js 复制代码
export default {
  // ...
  externalsType: "commonjs",
  externals: {
    "fs-extra": "fs-extra",
  },
};

将生成类似以下的内容:

js 复制代码
import fs from "fs-extra";

请注意,输出 bundle 中会有一个 require()

externalsType.global

将 externals 的默认类型设置为 'global'。webpack 将读取 globalObject 上的一个全局变量作为外部依赖。

示例

js 复制代码
import jq from "jquery";

jq(".my-element").animate(/* ... */);

webpack.config.js

js 复制代码
export default {
  // ...
  externalsType: "global",
  externals: {
    jquery: "$",
  },
  output: {
    globalObject: "global",
  },
};

将生成类似以下的内容:

js 复制代码
const jq = globalThis.$;

jq(".my-element").animate(/* ... */);

externalsType.module

将 externals 的默认类型设置为 'module'。webpack 将为模块中使用的外部依赖生成类似 import * as X from '...' 的代码。

请确保先启用 experiments.outputModule,否则 webpack 将抛出错误。

示例

js 复制代码
import jq from "jquery";

jq(".my-element").animate(/* ... */);

webpack.config.js

js 复制代码
export default {
  experiments: {
    outputModule: true,
  },
  externalsType: "module",
  externals: {
    jquery: "jquery",
  },
};

将生成类似以下的内容:

js 复制代码
import * as __WEBPACK_EXTERNAL_MODULE_jquery__ from "jquery";

const jq = __WEBPACK_EXTERNAL_MODULE_jquery__.default;
jq(".my-element").animate(/* ... */);

请注意,输出 bundle 中会有一个 import 语句。

保留阶段关键字

5.107.0+

defersource 导入阶段关键字会像 导入属性 一样保留在 module 外部依赖上。针对 module 外部依赖的静态 import defer * as ns from "mod" 会作为原生 import defer * as ... 语句输出,而 import source v from "mod" 会变为 import source ... from "mod"。使用两种不同阶段导入相同外部依赖将产生不同的 ExternalModule 实例,因此不会静默丢失任何阶段。

js 复制代码
// 输入
import defer * as ns from "external-mod";
import source v from "external-mod";

// 输出(使用 externalsType: "module")
import defer * as ns from "external-mod";
import source v from "external-mod";

externalsType.import

5.94.0+

将 externals 的默认类型设置为 'import'。webpack 将为模块中使用的外部依赖生成类似 import('...') 的代码。

示例

js 复制代码
async function foo() {
  const jq = await import("jQuery");
  jq(".my-element").animate(/* ... */);
}

webpack.config.js

js 复制代码
export default {
  externalsType: "import",
  externals: {
    jquery: "jquery",
  },
};

将生成类似下面的内容:

js 复制代码
const __webpack_modules__ = {
  jQuery: (module) => {
    module.exports = import("jQuery");
  },
};

// webpack 运行时...

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

请注意,输出 bundle 中会有一个 import() 语句。

保留阶段关键字

5.107.0+

当导入函数名是默认的 "import" 时,动态的 import.defer(...)import.source(...) 也会保留在 import 外部依赖上。阶段关键字会输出到结果中,而不是被剥离。

js 复制代码
// 输入
const ns = await import.defer("external-mod");
const src = await import.source("external-mod");

// 输出(使用 externalsType: "import")
const ns = await import.defer("external-mod");
const src = await import.source("external-mod");

externalsType.module-import

5.94.0+

将 externals 的默认类型设置为 'module-import'。这结合了 'module''import'。webpack 会自动检测导入语法的类型,对静态导入设置为 'module',对动态导入设置为 'import'

如果存在静态导入,请确保先启用 experiments.outputModule,否则 webpack 将抛出错误。

示例

js 复制代码
import { attempt } from "lodash";

async function foo() {
  const jq = await import("jQuery");
  attempt(() => jq(".my-element").animate(/* ... */));
}

webpack.config.js

js 复制代码
export default {
  externalsType: "module-import",
  externals: {
    jquery: "jquery",
    lodash: "lodash",
  },
};

将生成类似下面的内容:

js 复制代码
import * as __WEBPACK_EXTERNAL_MODULE_lodash__ from "lodash";

const lodash = __WEBPACK_EXTERNAL_MODULE_jquery__;

const __webpack_modules__ = {
  jQuery: (module) => {
    module.exports = import("jQuery");
  },
};

// webpack 运行时...

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

请注意,输出 bundle 中会有一个 importimport() 语句。

当模块不是通过 importimport() 导入时,webpack 将使用 "module" 外部类型作为回退。如果你希望使用不同类型的外部依赖作为回退,可以在 externals 选项中用函数指定。例如:

js 复制代码
export default {
  externalsType: "module-import",
  externals: [
    function ({ request, dependencyType }, callback) {
      if (dependencyType === "commonjs") {
        return callback(null, `node-commonjs ${request}`);
      }
      callback();
    },
  ],
};

externalsType.node-commonjs

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

示例

js 复制代码
import jq from "jquery";

jq(".my-element").animate(/* ... */);

webpack.config.js

js 复制代码
module.export = {
  experiments: {
    outputModule: true,
  },
  externalsType: "node-commonjs",
  externals: {
    jquery: "jquery",
  },
};

将生成类似以下的内容:

js 复制代码
import { createRequire } from "node:module";

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

请注意,输出 bundle 中会有一个 import 语句。

当依赖项依赖 Node.js 内置模块或需要 CommonJS 风格的 require 函数来保留原型时,这很有用,对于 util.inherits 等函数是必需的。更多细节请参考 此问题

对于依赖原型结构的代码,例如:

js 复制代码
function ChunkStream() {
  Stream.call(this);
}
util.inherits(ChunkStream, Stream);

你可以使用 node-commonjs 来确保原型链得以保留:

js 复制代码
const { builtinModules } = require("node:module");

export default {
  experiments: { outputModule: true },
  externalsType: "node-commonjs",
  externals: ({ request }, callback) => {
    if (request.startsWith("node:") || builtinModules.includes(request)) {
      return callback(null, `node-commonjs ${request}`);
    }
    callback();
  },
};

这会产生类似以下的内容:

js 复制代码
import { createRequire as __WEBPACK_EXTERNAL_createRequire } from "node:module";

const __webpack_modules__ = {
  // ...
  /***/ 2613: /***/ (module) => {
    module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)(
      "stream",
    );

    /***/
  },
  // ...
};

这种设置保持了原型结构,解决了 Node.js 内置模块的问题。

externalsType.promise

将 externals 的默认类型设置为 'promise'。webpack 将读取一个全局变量(类似于 'var')作为外部依赖,并 await 它。

示例

js 复制代码
import jq from "jquery";

jq(".my-element").animate(/* ... */);

webpack.config.js

js 复制代码
export default {
  // ...
  externalsType: "promise",
  externals: {
    jquery: "$",
  },
};

将生成类似以下的内容:

js 复制代码
const jq = await $;

jq(".my-element").animate(/* ... */);

externalsType.self

将 externals 的默认类型设置为 'self'。webpack 将读取 self 对象上的一个全局变量作为外部依赖。

示例

js 复制代码
import jq from "jquery";

jq(".my-element").animate(/* ... */);

webpack.config.js

js 复制代码
export default {
  // ...
  externalsType: "self",
  externals: {
    jquery: "$",
  },
};

将生成类似以下的内容:

js 复制代码
const jq = globalThis.$;

jq(".my-element").animate(/* ... */);

externalsType.script

将 externals 的默认类型设置为 'script'。webpack 将使用 HTML <script> 元素加载外部依赖,该元素会暴露预定义的全局变量。脚本加载完成后,<script> 标签将被移除。

语法

js 复制代码
export default {
  externalsType: "script",
  externals: {
    packageName: [
      "http://example.com/script.js",
      "global",
      "property",
      "property",
    ], // 属性是可选的
  },
};

如果你不打算指定任何属性,也可以使用简写语法:

js 复制代码
export default {
  externalsType: "script",
  externals: {
    packageName: "global@http://example.com/script.js", // 这里没有属性
  },
};

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

示例

让我们从 CDN 加载一个 lodash

webpack.config.js

js 复制代码
export default {
  // ...
  externalsType: "script",
  externals: {
    lodash: ["https://cdn.jsdelivr.net/npm/lodash@4.17.19/lodash.min.js", "_"],
  },
};

然后在代码中使用它:

js 复制代码
import _ from "lodash";

console.log(_.head([1, 2, 3]));

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

js 复制代码
export default {
  // ...
  externalsType: "script",
  externals: {
    lodash: [
      "https://cdn.jsdelivr.net/npm/lodash@4.17.19/lodash.min.js",
      "_",
      "head",
    ],
  },
};

当你 import lodash 时,局部变量 head 和全局变量 window._ 都会被暴露:

js 复制代码
import head from "lodash";

console.log(head([1, 2, 3])); // 这里输出 1
console.log(globalThis._.head(["a", "b"])); // 这里输出 a

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

提示: output.chunkLoadTimeoutoutput.crossOriginLoadingoutput.scriptType 等选项也会对以这种方式加载的外部脚本产生影响。

警告: externalsType: "script" 使用经典(非模块)<script src="..."> 标签加载外部依赖。外部文件必须与经典脚本兼容(即没有顶层的 import / export)。如果加载的脚本包含 ESM 语法(例如 import * as b from "b.js"),浏览器将无法执行它,webpack 会报告脚本加载失败(通常会显示为 ScriptExternalLoadError: Loading script failed.)。

提示: 如果你需要模块到模块的依赖,最好使用 externalsType: "import" / externalsType: "module" / externalsType: "module-import"。如果必须保留 externalsType: "script",请先加载依赖并通过全局变量(例如 globalThis.SomeLib)访问它们,而不是使用 import

externalsType.this

将 externals 的默认类型设置为 'this'。webpack 将读取 this 对象上的一个全局变量作为外部依赖。

示例

js 复制代码
import jq from "jquery";

jq(".my-element").animate(/* ... */);

webpack.config.js

js 复制代码
export default {
  // ...
  externalsType: "this",
  externals: {
    jquery: "$",
  },
};

将生成类似以下的内容:

js 复制代码
const jq = this.$;

jq(".my-element").animate(/* ... */);

externalsType.var

将 externals 的默认类型设置为 'var'。webpack 将读取一个全局变量作为外部依赖。

示例

js 复制代码
import jq from "jquery";

jq(".my-element").animate(/* ... */);

webpack.config.js

js 复制代码
export default {
  // ...
  externalsType: "var",
  externals: {
    jquery: "$",
  },
};

将生成类似以下的内容:

js 复制代码
const jq = $;

jq(".my-element").animate(/* ... */);

externalsType.window

将 externals 的默认类型设置为 'window'。webpack 将读取 window 对象上的一个全局变量作为外部依赖。

示例

js 复制代码
import jq from "jquery";

jq(".my-element").animate(/* ... */);

webpack.config.js

js 复制代码
export default {
  // ...
  externalsType: "window",
  externals: {
    jquery: "$",
  },
};

将生成类似以下的内容:

js 复制代码
const jq = globalThis.$;

jq(".my-element").animate(/* ... */);

externalsPresets

object

为特定目标启用外部依赖预设。

选项 描述 输入类型
electron 将主进程和预加载上下文中的常见 electron 内置模块(如 electronipcshell)视为外部依赖,并在使用时通过 require() 加载。 boolean
electronMain 将主进程上下文中的 electron 内置模块(如 appipc-mainshell)视为外部依赖,并在使用时通过 require() 加载。 boolean
electronPreload 将预加载上下文中的 electron 内置模块(如 web-frameipc-renderershell)视为外部依赖,并在使用时通过 require() 加载。 boolean
electronRenderer 将渲染进程上下文中的 electron 内置模块(如 web-frameipc-renderershell)视为外部依赖,并在使用时通过 require() 加载。 boolean
node 将 node.js 内置模块(如 fspathvm)视为外部依赖,并在使用时通过 require() 加载。 boolean
nwjs NW.js 遗留的 nw.gui 模块视为外部依赖,并在使用时通过 require() 加载。 boolean
web http(s)://...std:... 引用视为外部依赖,并在使用时通过 import 加载。(请注意,这会改变执行顺序,因为外部依赖会在 chunk 中的任何其他代码之前执行) boolean
webAsync http(s)://...std:... 引用视为外部依赖,并在使用时通过 async import() 加载。(请注意,此外部类型是一个 async 模块,会对执行产生各种影响) boolean

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

示例

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

webpack.config.js

js 复制代码
export default {
  // ...
  externalsPresets: {
    node: true,
  },
};

帮助我们改进文档

发现翻译问题或内容错误?请告诉我们。