Webpack 学习笔记(三 - 创建库)

除了打包应用程序,webpack 还可以打包 JavaScript 库。以下指南适用于希望简化打包策略的库作者。

创建一个库

假设正在编写一个名为 webpack-numbers 的库,用于将数字 1 到 5 转换为文本表示,反之亦然,例如将 2 转换为 'two'。

基本的项目结构应该与下面类似:

project

+  |- webpack.config.js
+  |- package.json
+  |- /src
+    |- index.js
+    |- ref.json

使用 npm 初始化项目,然后安装 webpackwebpack-cli 与 lodash

npm init -y
npm install --save-dev webpack webpack-cli lodash

注意:由于不需要将 lodash 一同打包到库中,因此应该将其安装为 devDependencies 而非 dependencies,否则库体积会变得很大。

src/ref.json

[
  {
    "num": 1,
    "word": "One"
  },
  {
    "num": 2,
    "word": "Two"
  },
  {
    "num": 3,
    "word": "Three"
  },
  {
    "num": 4,
    "word": "Four"
  },
  {
    "num": 5,
    "word": "Five"
  },
  {
    "num": 0,
    "word": "Zero"
  }
]

src/index.js

import _ from 'lodash';
import numRef from './ref.json';

export function numToWord(num) {
  return _.reduce(
    numRef,
    (accum, ref) => {
      return ref.num === num ? ref.word : accum;
    },
    ''
  );
}

export function wordToNum(word) {
  return _.reduce(
    numRef,
    (accum, ref) => {
      return ref.word === word && word.toLowerCase() ? ref.num : accum;
    },
    -1
  );
}

webpack 配置

从如下 webpack 基本配置开始:

webpack.config.js

const path = require('path');

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

上述配置告诉 webpack 将 src/index.js 打包到 dist/webpack-numbers.js 中。

暴露库

到目前为止,一切都应该与打包应用程序一样,但是打包库有一个不同的地方——需要通过 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",
    },
  };

我们将入口暴露为 webpackNumbers,这样用户就可以通过脚本标签使用它:

<script src="https://example.org/webpack-numbers.js"></script>
<script>
  window.webpackNumbers.wordToNum('Five');
</script>

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

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

  • 在 CommonJS 模块中导入

    const webpackNumbers = require('webpack-numbers');
    // ……
    webpackNumbers.wordToNum('Two');
  • 在 AMD 模块中导入

    require(['webpackNumbers'], function (webpackNumbers) {
      // ……
      webpackNumbers.wordToNum('Two');
    });
  • 使用脚本标签

    <!DOCTYPE html>
    <html>
      ...
      <script src="https://example.org/webpack-numbers.js"></script>
      <script>
        // ……
        // 全局变量
        webpackNumbers.wordToNum('Five');
        // 属性处于 window 对象中
        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: 'webpackNumbers',
+    library: {
+      name: 'webpackNumbers',
+      type: 'umd',
+    },
   },
 };

现在 webpack 将打包它,使其可以通过 CommonJS、AMD 模块以及脚本标签使用。

提示

请注意,library 选项与 entry 配置项绑定。对于绝大多数的库,指定一个入口就已经足够了。尽管可能存在 multi-part library,但通过作为单个入口起点的 index 脚本 暴露部分导出会更简单。不推荐 使用数组作为 entry

外部化 lodash

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

使用 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: '_',
+     },
+   },
  };

上面的配置意味着这个库需要一个名为 lodash 的依赖,这个依赖在开发者环境中必须存在且可用。

外部化的限制

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

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

// ...

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

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

最终步骤

遵循 生产环境 指南中提到的步骤优化生产环境下的输出结果。那么此时还需要生成 bundle 的文件路径,并将其添加到 package.json 中的 main 字段中。

package.json

{
  ...
  "main": "dist/webpack-numbers.js",
  ...
}

或者也可以按照这个 指南 将其添加为标准模块:

{
  ...
  "module": "src/index.js",
  ...
}

此处的键 main 是参照 package.json 标准,而 module 是参照 这个 提案,此提案允许 JavaScript 生态系统升级使用 ES2015 模块,而不会破坏向后兼容性。

警告

module 属性应指向一个使用 ES2015 模块语法的脚本,但不包括浏览器或 Node.js 尚不支持的其他语法特性。这使得 webpack 本身就可以解析模块语法,如果用户只用到库的某些部分,则允许通过 tree shaking 打包更轻量的包。

现在便可以 将其发布为一个 npm 包,并且在 unpkg.com 找到它,并分发给用户。

提示

建议使用 MiniCssExtractPlugin 暴露与库关联的样式表,然后用户可以像使用其他样式表一样使用和加载这些样式表。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值