Webpack 外部扩展(Externals)

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

externals

string object function RegExp [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" rel="external nofollow" 
  integrity="sha256-slogkvB1K3VOkzAI8QITxV3VzpOnkeNVsKvtkYLMjfk="
  crossorigin="anonymous"
></script>

webpack.config.js

module.exports = {
  //...
  externals: {
    jquery: jQuery,
  },
};

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

import $ from jquery;

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

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

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

字符串

根据 externalsType,这可能是全局变量的名称(参见 global、this、 var , window) 或模块的名称(参见 amd, commonjs,  module, umd)。

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

module.exports = {
  //...
  externals: jquery,
};

equals to

module.exports = {
  //...
  externals: {
    jquery: jquery,
  },
};

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

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

module.exports = {
  //...
  externals: {
    jquery: commonjs jquery,
  },
};

[string]

module.exports = {
  //...
  externals: {
    subtract: [./math, subtract],
  },
};

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

当 externalsType 为 commonjs 时,此示例将转换为 ​require(./math).subtract;​,而当 externalsType 为 window 时,此示例将转换为 ​window["./math"]["subtract"];

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

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

对象

module.exports = {
  //...
  externals: {
    react: react,
  },

  // 或者

  externals: {
    lodash: {
      commonjs: lodash,
      amd: lodash,
      root: _, // 指向全局变量
    },
  },

  // 或者

  externals: {
    subtract: {
      root: [math, subtract],
    },
  },
};

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

函数

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

对于 webpack 外部化,通过定义函数来控制行为,可能会很有帮助。例如,webpack-node-externals 能够排除 node_modules 目录中所有模块,还提供一些选项,比如白名单 package(whitelist package)。

函数接收两个入参:

  • ctx (object):包含文件详情的对象。ctx.context (string): 包含引用的文件目录。ctc.request (string): 被请求引入的路径。ctx.contextInfo (object): 包含 issuer 的信息(如,layer 和 compiler)ctx.getResolve 5.15.0+: 获取当前解析器选项的解析函数。
  • callback (function (err, result, type)): 用于指明模块如何被外部化的回调函数

回调函数接收三个入参:

  • err (Error): 被用于表明在外部外引用的时候是否会产生错误。如果有错误,这将会是唯一被用到的参数。
  • result (string [string] object): 描述外部化的模块。可以接受其它标准化外部化模块格式,(string, [string],或  object)。
  • type (string): 可选的参数,用于指明模块的 external type(如果它没在 result 参数中被指明)。

作为例子,要外部化所有匹配一个正则表达式的引入,你可以像下面那样处理:

webpack.config.js

module.exports = {
  //...
  externals: [
    function ({ context, request }, callback) {
      if (/^yourregex$/.test(request)) {
        // 使用 request 路径,将一个 commonjs 模块外部化
        return callback(null, commonjs  + request);
      }

      // 继续下一步且不外部化引用
      callback();
    },
  ],
};

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

webpack.config.js

module.exports = {
  externals: [
    function (ctx, callback) {
      // 该外部化的模块,是一个 `commonjs2` 的模块,且放在 `@scope/library` 目录中
      callback(null, @scope/library, commonjs2);
    },
  ],
};

webpack.config.js

module.exports = {
  externals: [
    function (ctx, callback) {
      // 该外部化模块是一个全局变量叫作 `nameOfGlobal`.
      callback(null, nameOfGlobal);
    },
  ],
};

webpack.config.js

module.exports = {
  externals: [
    function (ctx, callback) {
      // 该外部化模块是一个在`@scope/library`模块里的命名导出(named export)。
      callback(null, [@scope/library, namedexport], commonjs);
    },
  ],
};

webpack.config.js

module.exports = {
  externals: [
    function (ctx, callback) {
      // 外部化模块是一个 UMD 模块
      callback(null, {
        root: componentsGlobal,
        commonjs: @scope/components,
        commonjs2: @scope/components,
        amd: components,
      });
    },
  ],
};

RegExp

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

webpack.config.js

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

这个示例中,所有名为 jQuery 的依赖(忽略大小写),或者 $,都会被外部化。

混用语法

有时你需要混用上面介绍的语法。这可以像以下这样操作:

webpack.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,
  ],
};

byLayer

function object

按层指定 externals:

webpack.config.js

module.exports = {
  externals: {
    byLayer: {
      layer: {
        external1: var 43,
      },
    },
  },
};

externalsType

string = var

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

支持的类型如下:

  • amd
  • amd-require
  • assign​ – same as var
  • commonjs
  • commonjs-module
  • global
  • import​ – uses import() to load a native EcmaScript module (async module)
  • jsonp
  • module
  • node-commonjs
  • promise​ – 与 var 相同,但是会 await 结果(适用于 async 模块)
  • self
  • system
  • script
  • this
  • umd
  • umd2
  • var
  • window

webpack.config.js

module.exports = {
  //...
  externalsType: promise,
};

externalsType.commonjs

将外部的默认类型指定为“commonjs”。Webpack 将为模块中使用的外部生成类似 const X = require(…) 的代码。

示例

import fs from fs-extra;

webpack.config.js

module.exports = {
  // ...
  externalsType: commonjs,
  externals: {
    fs-extra: fs-extra,
  },
};

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

const fs = require(fs-extra);

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

externalsType.global

将外部的默认类型指定为 global。Webpack 会将 external 作为全局变量读取到 globalObject。

示例

import jq from jquery;
jq(.my-element).animate(/* ... */);

webpack.config.js

module.exports = {
  // ...
  externalsType: global,
  externals: {
    jquery: $,
  },
  output: {
    globalObject: global,
  },
};

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

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

externalsType.module

将 externals 类型设置为 module,webpack 将会在 module 中为外部引用生成形如 import * as X from … 的代码。

确保首先把 experiments.outputModule 设置为 true, 否则 webpack 将会报错。

Example

import jq from jquery;
jq(.my-element).animate(/* ... */);

webpack.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.node-commonjs

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

Example

import jq from jquery;
jq(.my-element).animate(/* ... */);

webpack.config.js

module.export = {
  experiments: {
    outputModule: true,
  },
  externalsType: node-commonjs,
  externals: {
    jquery: jquery,
  },
};

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

import { createRequire } from module;

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

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

externalsType.promise

将外部的默认类型指定为 promise。Webpack 会将 external 读取为全局变量(类似于 var)并为它执行 await。

Example

import jq from jquery;
jq(.my-element).animate(/* ... */);

webpack.config.js

module.exports = {
  // ...
  externalsType: promise,
  externals: {
    jquery: $,
  },
};

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

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

externalsType.self

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

示例

import jq from jquery;
jq(.my-element).animate(/* ... */);

webpack.config.js

module.exports = {
  // ...
  externalsType: self,
  externals: {
    jquery: $,
  },
};

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

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

externalsType.script

将 external 的默认类型指定为 script。Webpack 会将 external 作为脚本加载,并使用 HTML <script> 元素暴露预定义的全局变量。<script> 标签将在脚本加载后被移除。

Syntax

module.exports = {
  externalsType: script,
  externals: {
    packageName: [
      http://example.com/script.js,
      global,
      property,
      property,
    ], // properties are optional
  },
};

如果你不打算定义任何属性,你可以使用简写形式:

module.exports = {
  externalsType: script,
  externals: {
    packageName: global@http://example.com/script.js, // no properties here
  },
};

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

示例:

从 CDN 加载 lodash:

webpack.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]));

下面示例是针对上面示例新增了属性配置:

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

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

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

externalsType.this

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

示例:

import jq from jquery;
jq(.my-element).animate(/* ... */);

webpack.config.js

module.exports = {
  // ...
  externalsType: this,
  externals: {
    jquery: $,
  },
};

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

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

externalsType.var

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

示例:

import jq from jquery;
jq(.my-element).animate(/* ... */);

webpack.config.js

module.exports = {
  // ...
  externalsType: var,
  externals: {
    jquery: $,
  },
};

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

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

externalsType.window

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

示例:

import jq from jquery;
jq(.my-element).animate(/* ... */);

webpack.config.js

module.exports = {
  // ...
  externalsType: window,
  externals: {
    jquery: $,
  },
};

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

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

externalsPresets

object

为特定的 target 启用 externals 的 preset。

选项描述输入类型
electron

将 main 和预加载上下文中常见的 electron 内置模块视为 external 模块(如 electronipc 或 shell),使用时通过 require() 加载。boolean
electronMain

将 main 上下文中的 electron 内置模块视为 external 模块(如 appipc-main 或 shell),使用时通过 require() 加载。boolean
electronPreload

将预加载上下文的 electron 内置模块视为 external 模块(如 web-frameipc-renderer 或 shell),使用时通过 require() 加载。boolean
electronRenderer

将 renderer 上下文的 electron 内置模块视为 external 模块(如 web-frameipc-renderer 或 shell),使用时通过 require() 加载。boolean
node

将 node.js 的内置模块视为 external 模块(如 fspath 或 vm),使用时通过 require() 加载。boolean
nwjs

将 NW.js 遗留的 nw.gui 模块视为 external 模块,使用时通过 require() 加载。boolean
web

将 http(s)://... 以及 std:... 视为 external 模块,使用时通过 import 加载。(注意,这将改变执行顺序,因为 external 代码会在该块中的其他代码执行前被执行)boolean
webAsync

将 http(s)://… 以及 std:… 的引用视为 external 模块,使用时通过 async import() 加载。(注意,此 external 类型为 async 模块,它对执行会产生各种副作用)boolean

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

示例:

使用 node 的 preset 不会构建内置模块,而会将其视为 external 模块,使用时通过 require() 加载。

webpack.config.js

module.exports = {
  // ...
  externalsPresets: {
    node: true,
  },
};

作者:terry,如若转载,请注明出处:https://www.web176.com/webpack/22661.html

(0)
打赏 支付宝 支付宝 微信 微信
terryterry
上一篇 2023年5月26日
下一篇 2023年5月26日

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注