to-zip-webpack-plugin
插件是一个webpack
打包产物(output目录)
的压缩插件。对于经常对打包目录进行压缩的同学来说,省掉了手动压缩的步骤。本文将会介绍本插件的基本用法以及插件的具体实现。
插件目前已放到github上,同时也发布到了npm
github地址:https://github.com/booms21/to...
插件的基本使用
首先我们建立一个简单的React + Webpack
的项目xxbot,然后在webpack.config.js加入
new ToZipWebpackPlugin()
这里不用加入任何配置,因为默认会去压缩build后的dist文件(output)。我们运行npm run build
命令,看到build完成后多了一个...3823.zip的压缩文件,打开这个文件,发现和打包后的产物dist文件夹一样。
当然也可以加入一些配置使压缩操作更加定制化:
比如如下配置,把dist压缩成时间戳.tar文件,并且额外压缩一个README.md文件,压缩到当前目录。
解压...7229.tar 和mymd.tar
,发现这两个文件内容没有问题。两个不同的压缩工作是互不影响的,当需要产出压缩文件此时就可以拿去用了,当然还有其他配置。
插件内部的实现
下面我们看一下这个插件的具体实现:
首先我们查看webpack的官网对插件的说明:
官方文档说是在plugin属性种传入的是一个插件的实例,而且也需要含有一个apply
方法。所以我们需要使用class的方式实现:
我们的插件主要的任务是压缩webpack output的产出文件,所以我们要在官方的插件列表中找到一个afterEmit
钩子来实现插件的功能(当文件输出到output目录之后进行压缩)
在入口文件index.js中创建一个ToZipWebpackPlugin类,并在构造函数中对所有参数设置默认值:
index.js
class ToZipWebpackPlugin {
constructor({
zlibLevel,
format,
fileName,
defaultFileName,
source,
log,
deleteFileList,
archive,
} = {}) {
const FILETYPE = ["zip", "tar"]; //支持的压缩文件类型
this.options = {};
this.timeFormatter = new TimeFormatter();
this.options.zlibLevel = isNaN(zlibLevel) ? 9 : zlibLevel; //默认压缩等级为9
this.options.format = FILETYPE.includes(format) ? format : "zip"; //默认类型为zip
this.options.fileName = isString(fileName)
? fileName
: this.getFileName("time"); //默认文件名为时间字符串
isString(defaultFileName)
? (this.options.fileName =
this.getFileName(defaultFileName) || this.options.fileName)
: this.options.fileName;
this.options.source = isString(source) ? source : "";
this.options.deleteFileList = deleteFileList;
this.options.archive = archive;
this.options.log = log;
}
delete(deleteFileList) {
return del(deleteFileList);
}
getFileName(type) {
const typeTable = {
timestamp: String(this.timeFormatter.getTimestamp()),
time: this.timeFormatter.getDateStr("yyyymmddhhMMss"),
uuid: v4(),
};
return typeTable[type];
}
apply(compiler) {
compiler.hooks.afterEmit.tapAsync(
"ToZipWebpackPlugin",
(complition, callback) => {
this.options.archive && doArchive(this.options); //压缩前的压缩操作
outputArchive(this.options, complition.options.output.path);
isArray(this.options.deleteFileList) &&
this.delete(this.options.deleteFileList);
callback();
}
);
}
}
在钩子回调中加入3个步骤:
压缩前执行的自定义压缩archive > 默认的output压缩 > 最后的删除文件
记得最后需要执行callback();
那么我们先来看一下outputArchive(打包产物output压缩功能):
outputArchive.js
const path = require("path");
const fs = require("fs");
const archiver = require("archiver");
const { logger } = require("./util");
const outputArchive = (options, outputPath) => {
const filename = options.fileName;
const abPath = path.join(outputPath, "/", filename + "." + options.format);
const sourceDir = path.join(path.relative(path.resolve(), outputPath));
const output = fs.createWriteStream(path.relative(outputPath, abPath));
//设置压缩格式
const archive = archiver(options.format, {
zlib: { level: options.zlibLevel }, // Sets the compression level.
});
archive.on("warning", function (err) {
options.log && logger(err);
if (err.code === "ENOENT") {
// log warning
} else {
// throw error
throw err;
}
});
archive.on("error", function (err) {
options.log && logger(err);
throw err;
});
archive.on("finish", () => {
const msg =
"Compression finish ! - " +
path.join(path.resolve(), path.relative(outputPath, abPath)) +
" Size: " +
(archive.pointer() / 1024 / 1024).toFixed(2) +
"M";
console.log(msg);
options.log && logger(msg);
});
archive.pipe(output);
archive.directory(sourceDir);
archive.finalize(); //压缩完成
};
module.exports = outputArchive;
过程比较简单,获取到outputPath并转成绝对路径abPath,然后使用archiver库进行压缩,当压缩完成的时候使用log4js进行日志的记录。
最后看一下doArchive的实现:
doArchive.js
const doArchive = (options) => {
if (isString(options.archive.source) && isString(options.archive.targetDir)) {
const abPath = path.join(
options.archive.targetDir,
"/",
options.archive.fileName + "." + options.format
); //目标压缩路径
const output = fs.createWriteStream(abPath);
//设置压缩格式
const archive = archiver(options.format, {
zlib: { level: options.zlibLevel }, // Sets the compression level.
});
// good practice to catch warnings (ie stat failures and other non-blocking errors)
archive.on("warning", function (err) {
options.log && logger(err);
if (err.code === "ENOENT") {
// log warning
} else {
// throw error
throw err;
}
});
archive.on("error", function (err) {
options.log && logger(err);
throw err;
});
archive.on("finish", () => {
const msg =
"Compression finish ! - " +
abPath +
" Size: " +
(archive.pointer() / 1024 / 1024).toFixed(2) +
"M";
console.log(msg);
options.log && logger(msg);
});
archive.pipe(output);
if (fs.lstatSync(options.archive.source).isFile()) {
const strum = fs.createReadStream(options.archive.source);
archive.append(strum, { name: path.basename(options.archive.source) });
} else {
archive.directory(path.relative(__dirname, options.archive.source)); //压缩源为目录时转为相对路径
}
archive.finalize(); //压缩完成
}
};
module.exports = doArchive;
基本压缩操作还是和outputArchive一样,但是doArchive是可以自定义压缩文件夹和压缩目标输出的目录的。
当archive.source是一个文件时需要使用archive.append进行压缩。否则为文件夹时才可使用archive.directory压缩目录。
到这里整个插件就完成了。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。