编写一个webpack插件

概述

插件向第三方开发者提供了 webpack 引擎中完整的能力。本文将分两部分,第一部分介绍下插件编写的基本要求及基础 demo,第二部分介绍一个实际的插件开发过程。对于webpack的插件机制建另一篇博文webpack插件机制

详述

最简插件

最低要求,定义一个具名函数,原型提供一个 apply 方法:

1
2
3
4
5
6
class DemoPlugin {
apply() {
console.log('hello demo-plugin');
}
}
module.exports = DemoPlugin;

使用:

1
2
3
4
5
6
7
8
9
10
11
// webpack.config.js
const DemoPlugin = require('./plugins/demo-plugin');

module.exports = {
...
plugins: [
new DemoPlugin({
name: 'jovy'
})
]
}

基本结构

  • 一个具名 JavaScript 函数。
  • 在它的原型上定义 apply 方法。
  • 指定一个触及到 webpack 本身的 事件钩子。
  • 操作 webpack 内部的实例特定数据。
  • 在实现功能后调用 webpack 提供的 callback。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 一个 JavaScript class
class MyExampleWebpackPlugin {
// 将 `apply` 定义为其原型方法,此方法以 compiler 作为参数
apply(compiler) {
// 指定要附加到的事件钩子函数
compiler.hooks.emit.tapAsync(
'MyExampleWebpackPlugin',
(compilation, callback) => {
console.log('This is an example plugin!');
console.log(
'Here’s the `compilation` object which represents a single build of assets:',
compilation
);

// 使用 webpack 提供的 plugin API 操作构建结果
compilation.addModule(/* ... */);

callback();
}
);
}
}

实战一个压缩资源为 zip 的插件

知识准备

通过 Compilation 进行文件写入

Compilation 上的 assets 可以用于文件写入,可以将 zip 资源包设置到 compilation.assets 对象上,文件写入需要使用 webpack-sources

1
2
3
4
5
6
7
8
9
10
11
12
13
const { RawSource } = require('webpack-sources');
module.exports = class DemoPlugin {
constructor(options) {
this.options = options;
}
apply(compiler) {
const { name } = this.options;
compiler.hooks.emit.tapAsync('ZipPlugin', (compilation, callback) => {
compilation.assets[name] = new RawSource('demo');
cb();
});
}
};

Node.js 里面将文件压缩为 zip 包

使用jszip

1
2
3
4
5
6
7
8
var zip = new JSZip();
zip.file('Hello.txt', 'Hello World\n');
var img = zip.folder('images');
img.file('smile.gif', imgData, { base64: true });
zip.generateAsync({ type: 'blob' }).then(function(content) {
// see FileSaver.js
saveAs(content, 'example.zip');
});

环境搭建

一个基础的 webpack 环境即可,目录示例如下:
目录结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// package.json
{
"name": "my-plugin",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"webpack": "^4.40.2",
"webpack-cli": "^3.3.9"
},
"dependencies": {
"jszip": "^3.2.2",
"webpack-sources": "^1.4.3"
}
}

具体编写

安装具体 node 包及配置 scripts:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// package.json
{
"name": "my-plugin",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"webpack": "^4.40.2",
"webpack-cli": "^3.3.9"
},
"dependencies": {
"jszip": "^3.2.2",
"webpack-sources": "^1.4.3"
}
}

ZipPlugin插件编写:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// zip-plugin.js
const JSZip = require('jszip');
const { RawSource } = require('webpack-sources');

const zip = new JSZip();

class ZipPlugin {
constructor(options) {
this.options = options;
}

apply(compiler) {
compiler.hooks.emit.tapAsync('ZipPlugin', (compilation, callback) => {
const folder = zip.folder(this.options.filename);
for (let filename in compilation.assets) {
let source = compilation.assets[filename].source();
folder.file(filename, source);
}

zip
.generateAsync({
type: 'nodebuffer'
})
.then(content => {
const outputPath = this.options.filename + '.zip';

compilation.assets[outputPath] = new RawSource(content);

callback();
});
});
}
}

module.exports = ZipPlugin;

ZipPlugin插件使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const path = require('path');
const ZipPlugin = require('./plugins/zip-plugin');

module.exports = {
mode: 'production',
entry: './src/index.js',
output: {
filename: 'main.js',
path: path.join(__dirname, './dist')
},
plugins: [
new ZipPlugin({
filename: 'offline'
})
]
};

ZipPlugin测试:

1
npm run build

ZipPlugin使用效果

参考

https://webpack.docschina.org/contribute/writing-a-plugin/

https://webpack.docschina.org/api/compiler-hooks/#emit

https://webpack.docschina.org/api/plugins/#%E6%8F%92%E4%BB%B6%E7%B1%BB%E5%9E%8B-plugin-types-

《极客时间》