手写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的各个流程基本已经五全部概括,现在我们用一张图来总结一下webpack的工作流程