webpack学习记录(四)——自定义plugin的那些事

221 阅读3分钟

本文内容来自《深入浅出webpack》

Webpack 通过 Plugin 机制让其更加灵活,以适应各种应用场景。 在 Webpack 运行的生命周期中会广播出许多事件,Plugin 可以监听这些事件,在合适的时机通过 Webpack 提供的 API 改变输出结果。

基础的Plugin

一个最基础的 Plugin 的代码是这样的:

class BasicPlugin{
  // 在构造函数中获取用户给该插件传入的配置
  constructor(options){
  }

  // Webpack 会调用 BasicPlugin 实例的 apply 方法给插件实例传入 compiler 对象
  apply(compiler){
    compiler.plugin('compilation',function(compilation) {
    })
  }
}

// 导出 Plugin
module.exports = BasicPlugin;

在使用这个 Plugin 时,相关配置代码如下:

const BasicPlugin = require('./BasicPlugin.js');
module.export = {
  plugins:[
    new BasicPlugin(options),
  ]
}

Webpack 启动后,在读取配置的过程中会先执行new BasicPlugin(options)初始化一个BasicPlugin获得其实例。在初始化compiler对象后,再调用basicPlugin.apply(compiler)给插件实例传入compiler对象。插件实例在获取到compiler对象后,就可以通过compiler.plugin(事件名称,回调函数)监听到Webpack 广播出来的事件。 并且可以通过 compiler 对象去操作Webpack。

Compiler 和 Compilation

在开发 Plugin 时最常用的两个对象就是 Compiler 和 Compilation,它们是 Plugin 和 Webpack 之间的桥梁。 Compiler 和 Compilation 的含义如下:

  • Compiler 对象包含了 Webpack 环境所有的的配置信息,包含 options,loaders,plugins 这些信息,这个对象在 Webpack 启动时候被实例化,它是全局唯一的,可以简单地把它理解为 Webpack 实例;
  • Compilation 对象包含了当前的模块资源、编译生成资源、变化的文件等。当 Webpack 以开发模式运行时,每当检测到一个文件变化,一次新的 Compilation 将被创建。Compilation 对象也提供了很多事件回调供插件做扩展。通过 Compilation 也能读取到 Compiler 对象。

Compiler 和 Compilation 的区别在于:Compiler 代表了整个 Webpack 从启动到关闭的生命周期,而 Compilation 只是代表了一次新的编译。

事件流

Webpack 在运行过程中会广播事件,插件只需要监听它所关心的事件,就能加入到webpack的事件流中,去改变生产线的运作。 Webpack 的事件流机制保证了插件的有序性,使得整个系统扩展性很好。

Webpack 的事件流机制应用了观察者模式,和 Node.js 中的 EventEmitter 非常相似。 Compiler 和 Compilation 都继承自 Tapable,可以直接在 Compiler 和 Compilation 对象上广播和监听事件,方法如下:

/**
* 广播出事件
* event-name 为事件名称,注意不要和现有的事件重名
* params 为附带的参数
*/
compiler.apply('event-name',params);

/**
* 监听名称为 event-name 的事件,当 event-name 事件发生时,函数就会被执行。
* 同时函数中的 params 参数为广播事件时附带的参数。
*/
compiler.plugin('event-name',function(params) {

});

同理,compilation.apply 和 compilation.plugin 使用方法和上面一致。 更多事件流参考webpack中文网

实战

编写一个EndWebpackPlugin,在Webpack 成功编译或者编译失败时执行对应的回调函数。

1.创建一个my-plugin文件

在index.js中写入如下:

class EndWebpackPlugin{
  constructor(doneCallback,failCallback){
   // 存下在构造函数中传入的回调函数
    this.doneCallback=doneCallback;
    this.failCallback=failCallback;
  }

  apply(compiler){
    compiler.plugin('done',(stats)=>{
    // 在 done 事件中回调 doneCallback
      this.doneCallback(stats)
    });

    compiler.plugin('failed',(err)=>{
    // 在 failed 事件中回调 failCallback
      this.failCallback(err)
    });
  }
}

module.exports = EndWebpackPlugin

2.在webpack.config.js中配置

module.exports = {
  entry:{
    index:'./src/index.js',
  },

  plugins:[
    new EndWebpackPlugin((stats) => {
      console.info('已成功构建',stats)
    }, (err) => {
      
      console.error('构建失败',err);        
    })
  ]
};

3.运行webpack