除了打包应用程序,webpack
还可以用于打包 JavaScript library
。以下适用于希望简化打包策略的 library 作者。
假设我们正在编写一个名为 my-math
的小的 library,包含加法和减法。
基本的项目结构可能如下所示:
|- webpack.config.js
|- package.json
|- /test // 测试 library
|- index.html
|- /src
|- index.js
使用 npm 初始化项目,然后安装 webpack
,webpack-cli
和 lodash
:
npm init -y
npm install --save-dev webpack webpack-cli
src/index.js
export const add = (x, y) => x + y;
export const subtract = (x, y) => x - y;
webpack.config.js
const path = require('path');
module.exports = {
mode: 'development',
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'myMath.js',
},
};
在上面的例子中,我们将通知 webpack 将 src/index.js
打包到 dist/myMath.js
中。
到目前为止,一切都应该与打包应用程序一样,这里是不同的部分 - 我们需要通过 output.library
配置项暴露从入口导出的内容。
webpack.config.js
const path = require('path');
module.exports = {
mode: 'production',
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'myMath.js',
+ library: "myMath",
},
};
我们导出了 myMath
以便用户可以通过脚本标签使用它:
test/index.html
<script src="../dist/myMath.js">script>
<script>
myMath.add(1, 2);
script>
然而它只能通过被脚本标签引用而发挥作用,它不能运行在 CommonJS
、AMD
、Node.js
、 ES module
等环境中。
该特性仍然是实验性的,并且没有完全支持,所以请确保事先启用 experiments.outputModule。除此之外,你可以在 这里 追踪开发进度。
const path = require('path');
module.exports = {
mode: 'production',
entry: './src/index.js',
experiments: {
outputModule: true,
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'myMath.js',
library: {
// name: 'myMath', // 这里不要执行 name
type: 'module',
},
},
};
执行 npx webpack
test/index.html
<script type="module">
import { add } from '../dist/myMath.js'
console.log(add(1, 2));
</script>
可以看到能正常输出
一个库作者,我们希望它能够以不同的环境,方式,用户应该能够通过以下使用打包后的库:
CommonJS module require:
const myMath = require('myMath');
// ...
myMath.add(1, 2);
AMD module require:
require(['myMath'], function (myMath) {
// ...
myMath.add(1, 2);
});
script tag:
DOCTYPE html>
<html>
...
<script src="https://example.org/myMath.js">script>
<script>
// ...
// 全局变量
myMath.add(1, 2);
// 挂载到window上的属性
window.myMath.subtract(2, 2);
// ...
script>
html>
我们更新 output.library
配置项,将其 type
设置为 'umd'
:
const path = require('path');
module.exports = {
mode: 'development',
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'myMath.js',
library: {
name: 'myMath',
type: 'umd',
},
globalObject: 'this',
},
};
string = 'window'
当输出为 library 时,尤其是当 libraryTarget
为 'umd'
时,此选项将决定使用哪个全局对象来挂载 library。为了使 UMD 构建在浏览器和 Node.js 上均可用,应将 output.globalObject
选项设置为 'this'
。对于类似 web 的目标,默认为 self
。
现在 webpack 将打包一个库,其可以与 CommonJS、AMD 以及 script 标签使用。
当我们在创建 library
的时候可能会用到一些第三方 library
,比如 loadsh
npm i loadsh -S
src/index.js
import _ from 'lodash';
export const add = (x, y) => _.add(x, y);
export const subtract = (x, y) => x - y;
执行 npx webpack
,你会发现创建了一个体积相当大的文件。如果你查看这个文件,会看到 lodash 也被打包到代码中。在这种场景中,我们更倾向于把 lodash
当作 peerDependency
。也就是说,consumer(使用者) 应该已经安装过 lodash
。因此,你就可以放弃控制此外部 library ,而是将控制权让给使用 library 的 consumer。
这可以使用 externals
配置来完成:
webpack.config.js
const path = require('path');
module.exports = {
mode: 'development',
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'myMath.js',
library: {
name: "myMath",
type: "umd"
},
globalObject: 'this',
},
+ externals: {
+ lodash: {
+ commonjs: 'lodash',
+ commonjs2: 'lodash',
+ amd: 'lodash',
+ root: '_',
+ },
+ },
};
这意味着你的 library 需要一个名为 lodash
的依赖,这个依赖在 宿主环境 中必须存在且可用。
package.json
{
...
"main": "dist/myMath.js",
...
}
发布 npm publish
npm(三):npm包发布、更新、废弃
Tip
为了暴露和 library 关联着的样式表,你应该使用
MiniCssExtractPlugin
。然后,用户可以像使用其他样式表一样使用和加载这些样式表。
待续:【webpack 创建 library】实战篇