zoukankan      html  css  js  c++  java
  • 手写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的工作流程

    大致流程:

     

    1. 初始化参数:从配置文件和Shell语句中读取与合并参数,得出最终的参数;
    2. 开始编译: 用上一步得到的参数初始化Complier对象,加载所有配置的插件,执行对象的run方法开始执行编译;
    3. 确定入口: 根据配置中的entry找出所有入口文件;
    4. 编译模块:从入口文件出发,调用所有配置的Loader对模块进行翻译,再找出该模块依赖的模块,再递归本步骤知道所有入口依赖的文件都经过了本步骤的处理;
    5. 完成模块编译: 在经过第4步使用Loader翻译完所有模块后,得到了每个模块被翻译后的最终内容以及他们之间的依赖关系;
    6. 输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的Chunk,再把每个Chunk转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会;
    7. 输出完成: 在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统。

    在以上过程中,webpack会在特定的时间点广播出特定的时间,插件在监听到感兴趣的时间后会执行特定的逻辑,并且插件可以调用Webpack提供的API改变Webpack的运行结果。

    不积跬步无以至千里
  • 相关阅读:
    实用JS代码
    javascript-table出现滚动条表格自动对齐
    Javascript-关于for in和forEach
    Javascript-关于break、continue、return语句
    如何编写可怕的Java代码?
    请停止编写这么多的for循环!
    如何优雅地在Stack Overflow提问?
    为什么阿里巴巴Java开发手册中强制要求不要在foreach循环里进行元素的remove和add操作?
    Java异常处理只有Try-Catch吗?
    看完这篇还不会用Git,那我就哭了!
  • 原文地址:https://www.cnblogs.com/lyt0207/p/12584240.html
Copyright © 2011-2022 走看看