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压缩目录。
到这里整个插件就实现了。