沐光

记录在前端之路的点点滴滴

webpack library

前言

虽然先前有过使用 webpack 构建包的经验,但是最近同事问我 library 相关知识时,还是支支吾吾答不上来。想了想,知其然而不知其所以然,这和没学没有什么区别啊。所以,经过一番努力,总结了一点关于 library 的知识,这里就做个笔记,以做备忘吧。

配置目的

在开发 npm 库时,我们有时候需要考虑配置多场景的引入方式,比如: AMD、CommonJS、ES6 Moudule、NodeJs/Window 等等。那么为了能够支持这么些引入方式,library 便是 webpack 为我们提供的简化打包策略的方法,使用 library 的配置方式可以让我们专注于 ES6 的写法,而编译部分就不做过多考虑了。

由于 ECMAScript 越来越普及,此处仅介绍 ECMAScript 模块语法配置需要注意的问题。

文件配置

单文件配置

单文件 library 的配置就非常简单了,官方就有很好的例子,这里就先贴一下:

1
2
3
4
5
6
7
8
9
10
11
12
var path = require('path');

moudule.export = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'webpack-numbers.js',
library: 'webpack-demo',
// 支持 AMD 和 CommonJS
libraryTarget: "umd"
}
}

对于如今的项目来说,如果不是对引入方式有特别要求,建议使用 umd 方式;如果有明确指定环境时,再换成对应配置即可。

多文件配置

多文件配置唯一注意的是文件的引入,以及默认 library 名同样需要设置成动态的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var path = require('path');
// 多入口文件 JSON 对象
var components = require('./components.json');

moudule.export = {
entry: components,
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js',
// 每个文件的导出对象名与文件名保持一致
library: '[name]',
// 支持 AMD 和 CommonJS
libraryTarget: "umd",
// ES6 编写的组件,如果导出为 export default
// 如果文件引入测试有问题,可配置此属性
libraryExport: "default"
}
}

属性详解

其实配置什么的,随便在网上查查,再根据自身的理解尝试,都能有所成效。但是知其然我们也得知其所以然,这里我们就看看大家所关注的 library、libraryTarget、libraryExport 此三属性吧。

library

library 可设置的类型有两种,分别是:stringobject

当我们需要对不同环境设置不太一样的导出名时,我们才可能用到 object,例如:

1
2
3
4
5
6
7
8
// 仅此三属性
// 无 root 配置则其采用 commonjs 名,反之 commonjs 采用 root,此两属性必须配置其
// 一,否则会有全局污染的危险
library: {
commonjs: 'my-package-a',
amd: 'my-package-b',
root: 'my-package-c'
}

自测 demo 结果展示:

1
2
3
4
5
6
7
(function e(t, n) {
if (typeof exports === 'object' && typeof module === 'object')
module.exports = n();
else if (typeof define === 'function' && define.amd) define([], n);
else if (typeof exports === 'object') exports['my-package-a'] = n();
else t['my-package3-c'] = n();
})(/*...*/);

否则直接使用 string 模式即可。

注意,library 对象模式必须设置 libraryTarget 为 umd 模式;此外 library 名称最好使用驼峰式,毕竟生成的是变量。

疑问: umd 模式无论如何配置 amd,打包出的内容 amd 部分基本都是 define([], n);,即使配置成驼峰式的值,此外官方的例子上也没有对应值,不清楚是什么问题;但 amd 模式单独打包则有对应的配置名称。

此 amd 的配置与 amd-require 的结果很相似。

libraryTarget

libraryTarget 属性主要配置如何暴露我们的 library 名。其支持的值如下:

变量类型:

  • var(默认)
  • assign(产生隐含的全局变量,慎用)

对象类型:

  • this(绑定至 this 对象上)
  • window(浏览器环境的全局对象 )
  • global(node 环境的全局对象)
  • commonjs(绑定至 export 对象)
  • self(绑定至 self 对象)

模块类型:

  • amd(amd 引入模式)
  • umd(多模式并存)
  • commonjs-module(module.exports)
  • commonjs2(commonjs-module 的模块导出 + commonjs 对象,会忽略 library 名)
  • amd-require(立即加载版的 amd,会忽略 library 名)
  • system(systemJs 引入)
  • umd2

其他类型:

  • jsonp(library 为名的 jsonp 容器)

一般根据情况选取合适的 target 方式,通常来看 umd 已经能解决绝大多数通用的引入模式了。

umd2 结果和 umd 貌似差不多,个人暂未找到相应的解释

libraryExport

该属性主要是用于配置经由 libraryTarget 公开那些模块,默认为 undefined

默认情况下(假设 libraryTargetvar),变量导出的格式为:

1
var MyDefaultModule = _entry_return_;

如果需要显示表明导出哪些模块,我们可以这么配置:

1
2
3
4
5
6
7
// 指定模块
// libraryExport: 'MyModule'
var MyModule = _entry_return_.MyModule;

// 指定模块路径
// libraryExport: ['MyModule', 'MySubModule']
var MySubModule = _entry_return_.MyModule.MySubModule;

一般并不需要配置此属性,有一种情况是:当我们使用 ES6 编写模块时,有时候打包生成的文件在引入时,其内容呈现为:

1
2
3
{
default: {/* ... */}
}

毕竟 ES6 的 export defaultdefault 严格上来说也是一个变量名,因此为了修复此问题,我们可以配置 libraryExport: 'default',将默认导出分配给库目标

测试例子

该部分内容还是自己动手,配合官方文档理解起来会更快,可以拷贝该 测试 Demo 进行尝试。

参考文章