乐趣区

关于webpack:教你开发webpack插件tozipwebpackplugin

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

退出移动版