乐趣区

关于webpack:Webpack中的plugin插件机制

大家有没有遇到过这些问题:

  • webpack 打包之后的文件没有压缩
  • 动态文件要手动拷贝到输入目录
  • 代码中写了很多环境判断的多余代码

上一篇「webpack 外围个性」loader 说到 webpack 的 loader 机制,本文次要聊一聊另外一个外围个性:插件(plugin)。

插件机制就是为了实现我的项目中除了资源模块打包以外的其余自动化工作,解决上述的问题。

plugin 是用来扩大 webpack 性能的,通过在构建流程里注入钩子实现,它为 webpack 带来了很大的灵活性。

plugin 绝对于 loader 有哪些区别?

loader 是转换器,将一种文件编译转换为另一个文件,操作的是文件。例如:将 .less 文件转换成 .css 文件。

plugin 是扩展器,它是针对 loader 完结之后,打包的整个过程。它并不间接操作文件,而是基于事件机制工作。在 webpack 构建流程中的特定时机会播送对应的事件,插件能够监听这些事件的产生,在特定的机会做对应的事件。包含:打包优化,资源管理,注入环境变量。

plugin 该怎么配置呢?

例如 HtmlWebpackPlugin 能够为咱们生成一个 HTML 文件,其中包含应用 script 标签的 body 中的所有模块。看下如何配置:

const HtmlWebpackPlugin = require('html-webpack-plugin');

const webpackConfig = {
  ...
  plugins: [new HtmlWebpackPlugin()]
};

舒适提醒:loader 在 module.rules 中配置,作为模块的解析规定,类型为数组。每一项都是一个对象,外部蕴含了 test(类型文件)、loader、options(参数)等属性。plugin 则独自配置,类型为数组,每一项是一个 plugin 的实例,参数都通过构造函数传入。

有哪些常见的 plugin?

上面整顿的插件列表来自 webpack 中武官网,大家看见不相熟的 plugin 能够点击名称跳转,看一看,理解一下具体玩法。

名称 形容
AggressiveSplittingPlugin 将原来的 chunk 分成更小的 chunk
BabelMinifyWebpackPlugin 应用 babel-minify 进行压缩
BannerPlugin 在每个生成的 chunk 顶部增加 banner
CommonsChunkPlugin 提取 chunks 之间共享的通用模块
CompressionWebpackPlugin 事后筹备的资源压缩版本,应用 Content-Encoding 提供拜访服务
ContextReplacementPlugin 重写 require 表达式的推断上下文
CopyWebpackPlugin 将单个文件或整个目录复制到构建目录
DefinePlugin 容许在编译时 (compile time) 配置的全局常量
DllPlugin 为了极大缩小构建工夫,进行 dll 分包
EnvironmentPlugin DefinePlugin 中 process.env 键的简写形式。
ExtractTextWebpackPlugin 从 bundle 中提取文本(CSS)到独自的文件
HotModuleReplacementPlugin 启用模块热替换(Enable Hot Module Replacement – HMR)
HtmlWebpackPlugin 简略创立 HTML 文件,用于服务器拜访
I18nWebpackPlugin 为 bundle 减少国际化反对
IgnorePlugin 从 bundle 中排除某些模块
LimitChunkCountPlugin 设置 chunk 的最小 / 最大限度,以微调和管制 chunk
LoaderOptionsPlugin 用于从 webpack 1 迁徙到 webpack 2
MinChunkSizePlugin 确保 chunk 大小超过指定限度
NoEmitOnErrorsPlugin 在输入阶段时,遇到编译谬误跳过
NormalModuleReplacementPlugin 替换与正则表达式匹配的资源
NpmInstallWebpackPlugin 在开发时主动装置短少的依赖
ProvidePlugin 不用通过 import/require 应用模块
SourceMapDevToolPlugin 对 source map 进行更细粒度的管制
EvalSourceMapDevToolPlugin 对 eval source map 进行更细粒度的管制
UglifyjsWebpackPlugin 能够管制我的项目中 UglifyJS 的版本
ZopfliWebpackPlugin 通过 node-zopfli 将资源事后压缩的版本

怎么写一个 plugin?

在说怎么写插件之前,先简略介绍几个好玩的货色:tapablecompilercompilation。##### 参考 webpack 视频解说:进入学习

tapable

tapable 是一个相似于 nodejsEventEmitter 的库, 次要是管制钩子函数的公布与订阅。当然,tapable 提供的 hook 机制比拟全面,分为同步和异步两个大类(异步中又辨别异步并行和异步串行),而依据事件执行的终止条件的不同,由衍生出 Bail / Waterfall / Loop 类型。

根本应用:

const {SyncHook} = require('tapable');
const hook = new SyncHook(['name']);
hook.tap('hello', (name) => {console.log(`hello ${name}`);
});
hook.tap('hi', (name) => {console.log(`hi ${name}`);
});

hook.call('july');

// hello july
// hi july

小结:

tapable 根本逻辑是,先通过类实例的 tap 办法注册对应 hook 的处理函数,而后通过 call 办法触发回调函数。

compiler

compiler 对象蕴含 webpack 环境所有配置信息,蕴含 options、loaders 和 plugins 等信息。能够简略了解为 webpack 实例。代表整个 webpack 从启动到敞开的生命周期

compile 的外部实现:

class Compiler extends Tapable {constructor(context) {super();
    this.hooks = {/** @type {SyncBailHook<Compilation>} */
      shouldEmit: new SyncBailHook(["compilation"]),
      /** @type {AsyncSeriesHook<Stats>} */
      done: new AsyncSeriesHook(["stats"]),
      /** @type {AsyncSeriesHook<>} */
      additionalPass: new AsyncSeriesHook([]),
      /** @type {AsyncSeriesHook<Compiler>} */
      ...
    };
    ...
}

小结:

compile 继承了 tapable,而后在实例上绑定了一个 hook 对象。

compilation

compilation 对象蕴含了以后的模块资源、编译生成资源和变动的文件等。仅代表一次新的编译

compilation 的实现:

class Compilation extends Tapable {/**   * Creates an instance of Compilation.   * @param {Compiler} compiler the compiler which created the compilation   */
  constructor(compiler) {super();
    this.hooks = {/** @type {SyncHook<Module>} */
      buildModule: new SyncHook(["module"]),
      /** @type {SyncHook<Module>} */
      rebuildModule: new SyncHook(["module"]),
      /** @type {SyncHook<Module, Error>} */
      failedModule: new SyncHook(["module", "error"]),
      /** @type {SyncHook<Module>} */
      succeedModule: new SyncHook(["module"]),
      /** @type {SyncHook<Dependency, string>} */
      addEntry: new SyncHook(["entry", "name"]),
      /** @type {SyncHook<Dependency, string, Error>} */
    };
  }
}

当运行 webpack 开发环境中间件时,每当检测到一个文件变动,就会创立一个新的 compilation,从而生成一组新的编译资源。一个 compilation 对象体现了以后的模块资源、编译生成资源、变动的文件、以及被跟踪依赖的状态信息。compilation 对象也提供了很多要害机会的回调,以供插件做自定义解决时抉择应用。

热身

写一个最根底的 plugin

// 一个 JavaScript 命名函数。function SimplePlugin() {}

// 在插件函数的 prototype 上定义一个 `apply` 办法。SimplePlugin.prototype.apply = function (compiler) {
  // 指定一个挂载到 webpack 本身的事件钩子。compiler.plugin("webpacksEventHook", function (compilation /* 解决 webpack 外部实例的特定数据。*/,    callback) {console.log("This is an simple plugin!!!");

    // 性能实现后调用 webpack 提供的回调。callback();});
};

webpack 启动后,做了上面几件事件:

  • 在读取配置的过程中先执行 new SimplePlugin(),初始化一个 SimplePlugin 并取得其实例。
  • 在初始化 compiler 对象后,再调用 SimplePlugin.apply(compiler) 为插件实例传入 compiler 对象。
  • 插件实例在获取到 compiler 对象后,就能够通过 compiler.plugin("webpacksEventHook", function(compilation, callback){}) 监听到 webpack 播送的事件,并且通过 compiler 对象去操作 webpack。

实战

上面写一个实用的插件。

作用是在 webpack 马上敞开时做一些事件。例如告知用打包实现,是否执行胜利。或者执行一些 script 脚本。咱们将其命名为 AfterWebpackPlugin。用法如下:

module.exports = {
  plugins: [
    // 别离传入胜利和失败时的回调函数
    new AfterWebpackPlugin(() => {// 能够告诉用户构建胜利,执行公布脚本},
      (err) => {
        // 构建失败时,抛出异样
        console.error(err);
      }
    ),
  ],
};

这里实现过程须要借助以下两个钩子:

  • done:在胜利构建并且输入文件后,webpack 马上退出时产生。
  • failed:在构建异样时导致构建失败,webpack 马上退出时产生。

实现代码如下:

class AfterWebpackPlugin {constructor(doneCb, failedCb) {
    // 传入回调函数
    this.doneCb = doneCb;
    this.failedCb = failedCb;
  }

  apply(compiler) {compiler.plugin("done", (res) => {
      // webpack 生命周期 `done` 中的回调
      this.doneCb(res);
    });
    compiler.plugin("failed", (err) => {
      // webpack 生命周期 `failed` 中的回调
      this.failedCb(err);
    });
  }
}

module.exports = AfterWebpackPlugin;

开发插件小结:

  • 留神在 webpack 生命周期中找到适合的钩子去实现性能。
  • 留神了解 webpack 生命周期中各个钩子的轻微区别。

拓展

webpack 输入阶段 会产生的事件及解释如下:

事件名 解释
should-emit 所有须要输入的文件曾经生成,询问插件有哪些文件须要输入,有哪些不须要输入
emit 确定好要输入哪些文件后,执行文件输入,能够在这里获取和批改输入的内容
after-emit 文件输入结束
done 胜利实现一次残缺的编译和输入流程
failed 如果在编译和输入的流程遇到异样,导致 webpack 退出,就会间接跳转到本步骤,插件能够在本事件中获取具体谬误起因

系列文章

  • 「webpack 外围个性」loader
  • 「webpack 外围个性」插件(plugin)
  • 「webpack 外围个性」模块热替换(HMR)

感激

  • 如果本文对你有帮忙,就点个赞反对下吧!感激浏览。
退出移动版