Webpack 通过 Plugin 机制让其更加灵活,以适应各种应用场景。 在 Webpack 运行的生命周期中会广播出许多事件,Plugin 可以监听这些事件,在合适的时机通过 Webpack 提供的 API 改变输出结果。
1. 基础的plugin
一个最基础的 Plugin 的代码是这样
            
            
              javascript
              
              
            
          
          class BasicPlugin{
  // 在构造函数中获取用户给该插件传入的配置
  constructor(options){
  }
  // Webpack 会调用 BasicPlugin 实例的 apply 方法给插件实例传入 compiler 对象
  apply(compiler){
    compiler.plugin('compilation',function(compilation) {
    })
  }
}
// 导出 Plugin
module.exports = BasicPlugin;
        在使用这个 Plugin 时,相关配置代码如下:
            
            
              ini
              
              
            
          
          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。
2. Compiler和Compilation
在webpack的插件开发时,最经常用到的就是Compiler和Compilation,他们是webpack和plugin之间的桥梁
- Compiler包含了所有webpack的配置信息,包含options、loader、plugins等信息。这个对象在Webpack启动时被实例化,它是全局唯一的,可以简单地将它理解为Webpack的实例
 - Compilation对象包含了当前的模块资源、编译生成资源、变化的文件等。当Webpack以开发模式运行时,每当检测一个文件发生变化,便有一次新的Compilation被创建。Compilation对象也提供了很多事件回调供插件进行扩展。通过Compilation也能读取到Compiler对象
 
两者的区别可以理解为:Compiler代表了整个Webpack从启动到关闭的生命周期,Compilation只代表一次新的编译
3. 事件流
Webpack就像一条生产线,要经过一系列处理流程才能将源文件转化成输出结果。这个生产线上每个流程的职责都是单一的,多个流程之间存在依赖关系,只有在完成了当前处理后才能提交给下一个流程处理。
Webpack在运行过程中会广播事件,插件只需要监听它关心的事情,就能加入这条生产线了,这就是插件的运行原理。
我们可以通过Compiler和Compilation对象来广播和监听事件
            
            
              csharp
              
              
            
          
          // 广播事件
// event-name为事件名称,注意不要和现有的事件重名
// params为附带的参数
compiler.apply('event-name',params)
// 监听名称为event-name的事件,当event-name事件发生时,函数就会被执行
// 同时函数中的params参数为广播事件附带的参数
compiler.plugin('event-name',function(params){})
        同理,compilation.apply和compilation.plugin的使用方法和前面讲解一致
4. 开发一个插件
通过前面的知识,我们已经可以开发一个简单的插件了,比如当Webpack成功编译和输出文件后执行发布操作,将输出的文件上传到服务器,同时该插件还能区分Webpack构建是否成功。
要实现该插件,需要借助以下两个事件:
- done:在成功构建并且输出文件后,Webpack即将退出时发生
 - failed:在构建出现异常时导致构建失败,Webpack即将退出时发生
 
以下是该插件的用法
            
            
              javascript
              
              
            
          
          module.exports = {
    plugins: [
        //在初始化EndWebpackPlugin时传入两个参数,分别是成功时的回调函数和失败时的回调函数
        new EndWebpackPlugin(() => {
            //Webpack构建成功,并且文件在输出时会执行到这里,这里就可以做发布文件操作
        },(err) => {
            //Webpack构建失败,err是导致错误的原因
            console.error(err)
        })
    ]
}
        以下是实现这个插件
            
            
              javascript
              
              
            
          
          class EndWebpackPlugin {
    constructor(doneCallback,failCallback){
        // 保存在构造函数中传入的回调函数,即我们在webpack.config.js实例化EndWebpackPlugin时传进来的两个参数
        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
        我们还差最后一步,就是实现doneCallback,即将我们打包后的文件上传到我们的服务器,我们可以利用axios,通过post请求将打包出来的文件发送到我们服务器
            
            
              javascript
              
              
            
          
          //完整代码
//webpack.config.js
const axios = require('axios')
module.exports = {
    plugins: [
        //在初始化EndWebpackPlugin时传入两个参数,分别是成功时的回调函数和失败时的回调函数
        new EndWebpackPlugin((compilation,callback) => {
    
        var url = 'http://10.0.0.0:78/upload' //服务器的url
        var upload = async function(name,content){
            await axios.post(url,{name,content})
        }
        for(let filename in compilation.assets){
            let content = compilation.assets[filename]['source']()
            upload(filename,content)
            callback()
        }
    },(err) => {
            //Webpack构建失败,err是导致错误的原因
            console.error(err)
        })
    ]
}
//uploade.js
class EndWebpackPlugin {
    constructor(doneCallback,failCallback){
        // 保存在构造函数中传入的回调函数,即我们在webpack.config.js实例化EndWebpackPlugin时传进来的两个参数
        this.doneCallback = doneCallback
        this.failCallback = failCallback
    }
    apply(compiler){
        compiler.plugin('done',() => {
            // 在done事件中回调doneCallback
            this.doneCallback()
        }),
        compiler.plugin('failed',(err) => {
            // 在failed事件中回调failCallback
            this.failCallback(err)
        })
    }
}
// 导出插件
module.exports = EndWebpackPlugin