library & libraryTarget & externals & globalObject

module.exports = {
    output: {
        library: ,
        libraryTarget: "",
    }
}

library是指定义一个全局使用的名称变量

libraryTarget是指设置library的暴露方式,是commonjs、commonjs2、umd还是this、var等。

这三个属性的特征都是在公共对象上export出你的方法函数。特点无非是减少了变量冲突的可能性,但是依旧没有解决问题,只有global模式支持在node环境中,还必须设置target为node不然也是不支持的。

这两个属性,是符合模块化规范的产物,commonjs是在export上定义library设置的变量,commonjs2是用module.export直接export的。amd的依赖前置方案在浏览器、node中都必须额外引入RequireJS来使用。

amd那就是在define的方法中设置了library,这样就能符合RequireJS的使用规范。

兼容的模块化定义

但是如果你想做到这一点,必须要额外设置,umdNamedDefine: true,globalObject: ‘this’,umdNamedDefine为设置amd前置名称使用library设置的变量,globalObject为改变全局指向。

这样就能保证你的库在node和浏览器中通用了。当然便捷的引入一定会带来一定的冗余,这就看你如何取舍了。

最后建议,如果目标明确,我只是兼容nodejs,那么选择commonjs/commonjs2,如果只兼容浏览器,那就选择暴露变量的方式,如果想通用,那就选择umd的方式,对于不同的情况做多种处理方式,是非常明智的选择。

library

暴露一个自定义库

我们需要通过 output.library 配置项暴露从入口导出的内容。

webpack.config.js
  const path = require('path');

  module.exports = {
    entry: './src/index.js',
    output: {
      path: path.resolve(__dirname, 'dist'),
      filename: 'webpack-numbers.js',
+     library: "webpackNumbers",
    },
  };
<script src="https://example.org/webpack-numbers.js"></script>
<script>
  window.webpackNumbers.wordToNum('Five');
</script>

然而它只能通过被 script 标签引用而发挥作用,它不能运行在 CommonJS、AMD、Node.js 等环境中。

作为一个库作者,我们希望它能够兼容不同的环境,也就是说,用户应该能够通过以下方式使用打包后的库:

const webpackNumbers = require('webpack-numbers');
// ...
webpackNumbers.wordToNum('Two');
require(['webpackNumbers'], function (webpackNumbers) {
  // ...
  webpackNumbers.wordToNum('Two');
});
<!DOCTYPE html>
<html>
  ...
  <script src="https://example.org/webpack-numbers.js"></script>
  <script>
    // ...
    // Global variable
    webpackNumbers.wordToNum('Five');
    // Property in the window object
    window.webpackNumbers.wordToNum('Five');
    // ...
  </script>
</html>

我们更新 output.library 配置项,将其 type 设置为 'umd':

 const path = require('path');

 module.exports = {
   entry: './src/index.js',
   output: {
     path: path.resolve(__dirname, 'dist'),
     filename: 'webpack-numbers.js',
+    library: {
+      name: 'webpackNumbers',
+      type: 'umd',
+    },
   },
 };

现在 webpack 将打包一个库,其可以与 CommonJS、AMD 以及 script 标签使用。

外部化 lodash

现在,如果执行 webpack,你会发现创建了一个体积相当大的文件。如果你查看这个文件,会看到 lodash 也被打包到代码中。在这种场景中,我们更倾向于把 lodash 当作 peerDependency。也就是说,consumer(使用者) 应该已经安装过 lodash 。因此,你就可以放弃控制此外部 library ,而是将控制权让给使用 library 的 consumer。

这可以使用 externals 配置来完成:

webpack.config.js

  const path = require('path');

  module.exports = {
    entry: './src/index.js',
    output: {
      path: path.resolve(__dirname, 'dist'),
      filename: 'webpack-numbers.js',
      library: {
        name: "webpackNumbers",
        type: "umd"
      },
    },
+   externals: {
+     lodash: {
+       commonjs: 'lodash',
+       commonjs2: 'lodash',
+       amd: 'lodash',
+       root: '_',
+     },
+   },
  };

这意味着你的 library 需要一个名为 lodash 的依赖,这个依赖在 consumer 环境中必须存在且可用。

外部化的限制

对于想要实现从一个依赖中调用多个文件的那些 library:

import A from 'library/one';
import B from 'library/two';

// ...

无法通过在 externals 中指定整个 library 的方式,将它们从 bundle 中排除。而是需要逐个或者使用一个正则表达式,来排除它们。

module.exports = {
  //...
  externals: [
    'library/one',
    'library/two',
    // 匹配以 "library/" 开始的所有依赖
    /^library\/.+$/,
  ],
};

output.globalObject

当输出为 library 时,尤其是当 libraryTarget 为 'umd'时,此选项将决定使用哪个全局对象来挂载 library。为了使 UMD 构建在浏览器和 Node.js 上均可用,应将 output.globalObject 选项设置为 'this'。对于类似 web 的目标,默认为 self。

示例:

webpack.config.js

module.exports = {
  // ...
  output: {
    library: 'myLib',
    libraryTarget: 'umd',
    filename: 'myLib.js',
    globalObject: 'this',
  },
};

externals

我们通常在做项目时可能会把第三方库打包到bundle中,

如果不想把第三方库打包到bundle中,这就有了externals。官方的使用externals比较简单,只需三步

1.在HTML中引入第三方库的cdn

2.在webpack中配置externals

externals: {
    jquery: "jQuery",
}

3.在js中引用

const $ = require("jquery");
$("#content").html("<h1>hello world</h1>");

好,现在我们可以随心所欲的使用jquery插件并保证不会打包到bundle中。

详解:

防止将某些 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>

webpack.config.js

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

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

import $ from 'jquery';

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

具有外部依赖(external dependency)的 bundle 可以在各种模块上下文(module context)中使用,例如 CommonJS, AMD, 全局变量和 ES2015 模块。外部 library 可能是以下任何一种形式:

可以接受以下语法……

字符串

请查看上面的例子。属性名称是 jquery,表示应该排除 import $ from 'jquery' 中的 jquery 模块。为了替换这个模块,jQuery 的值将被用来检索一个全局的 jQuery 变量。换句话说,当设置为一个字符串时,它将被视为全局的(定义在上面和下面)。

另一方面,如果你想将一个符合 CommonJS 模块化规则的类库外部化,你可以提供外联类库的类型以及类库的名称。

如果你想将 fs-extra 从输出的 bundle 中剔除并在运行时中引入它,你可以如下定义:

module.exports = {
  // ...
  externals: {
    'fs-extra': 'commonjs2 fs-extra',  // 转化成 commonJS 格式
  },
};

这样的做法会让任何依赖的模块都不变,正如以下所示的代码:

import fs from 'fs-extra';

会将代码编译成:

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

补充

补充:四个关键 output 选项对照:

  • output.library:暴露给外部使用的全局变量名(如 MyLib)。
  • output.libraryExport:指定要导出模块的哪个 export(如 default)。
  • output.libraryTarget:暴露方式:
    • var(默认):把返回值挂到 var <library> 上。
    • this / window / global:挂到对应全局对象。
    • commonjs / commonjs2:以 module.exports 形式暴露(Node 端)。
    • umd:同时支持 AMD、CommonJS、全局变量(最通用,构建产物体积也最大)。
    • module(webpack 5):输出 ES Module 风格的 export。
  • output.globalObject:当 libraryTarget 是 umd/this 时使用的全局对象名,默认是 window(在 Node 环境下需设为 global)。
  • externals:声明"不打包的依赖",由使用方运行时提供(如通过 CDN 引入的 React)。

来源整理自:我的有道云笔记