手写webpack(三)增加plugin

plugin机制是webpack中另一个核心概念,它基于事件流框架tapable,通过plugin(插件)webpack可以实现loader所不能完成的复杂功能,使用plugin丰富的自定义API以及生命周期事件,可以控制webpack编译流程的每个环节,实现对webpack的自定义功能扩展。

首先安装tapable插件,并且引入,这里引入的是同步钩子

npm i tapable

let {SyncHook} = require(‘tapable‘)

接下来就是注册hooks

class Complier{
    constructor(config){
        this.config = config
        //需要保存入口文件的路径
        this.entryId  //主模块路径 "./src/index.js"
        //需要保存所有模块的依赖
        this.module = {}
        //入口路径
        this.entry = config.entry
        //工作目录 是指执行打包命令的文件夹地址 比如在d:/aa/b目录下执行 npm run build 那么cwd就是d:/aa/b
        this.root = process.cwd()

        this.hooks = {
            entryOption: new SyncHook(), //入口
            compile: new SyncHook(), //编译
            afterCompile: new SyncHook(), //编译完成
            afterPlugins: new SyncHook(), // 编译完插件之后
            run: new SyncHook(), //运行时
            emit: new SyncHook(), //发射文件
            done: new SyncHook() //完成
        }
        //如果传递了plugins参数
        let plugins = this.config.plugins
        if(Array.isArray(plugins)){
            plugins.forEach(plugin =>{
                plugin.apply(this)
            })
        }
        //在这里调用afterPlugins钩子
        this.hooks.afterPlugins.call()
    }

接着就是在编译的过程中调用钩子

run(){
        this.hooks.run.call()
        this.hooks.compile.call()
        //创建模块的依赖关系
        this.buildModule(path.resolve(this.root,this.entry),true) //true表示是主模块
        this.hooks.afterCompile.call() //编译完调用这个hooks
        //发射一个文件 打包后的文件
        this.emitFile()
        this.hooks.emit.call();
        this.hooks.done.call();
    }

在我们自己编写的打包文件self-pack.js文件里面调用entryOption这个钩子

//1.需要找到当前执行名的路径 拿到webpack.config.js
//1.1拿到文件路径
let path = require(‘path‘)
//1.2config配置文件
let config = require(path.resolve(‘webpack.config.js‘))
//1.3编译配置文件
let Compiler = require(‘./lib/Compiler‘)
let compiler = new Compiler(config)
compiler.hooks.entryOption.call() //在这儿调用这个hooks
//1.4运行
compiler.run()

接下来我们就在webpack.config.js文件里面模拟两个plugin

class p{
    apply(compiler){
        compiler.hooks.emit.tap(‘emit‘,function () {
            console.log(‘emit‘)
        })
    }
}
class p1{
    apply(compiler){
        compiler.hooks.afterPlugins.tap(‘afterPlugins‘,function () {
            console.log(‘afterPlugins‘)
        })
    }
}

plugins:[
        new p(),
        new p1()
    ]

调用结果如下:

手写webpack(三)增加plugin

 至此webpack的各个流程基本已经五全部概括,现在我们用一张图来总结一下webpack的工作流程

手写webpack(三)增加plugin