webpack学习5-手写plugin

1. webpack plugin的概念和其中关系

1. compiler 钩子

Compiler 模块是 webpack 的主要引擎,它扩展(extend)自 Tapable 类,用来注册和调用插件

2. Tapable类

tapable 是一个类似于nodejs 的EventEmitter 的库, 主要是控制钩子函数的发布与订阅,控制着webpack的插件系.webpack的本质就是一系列的插件运行.

2. 准备工作

1. 下载工具

1. webpack
2. webpack-cli
3. tapable // tapable类

2. Tapable类了解


// 分为两大类: 同步和异步 推荐异步
// 所有的钩子构造函数,都接受一个可选的参数, 这个参数最好是数组
const {
    SyncHook,  // 普通同步钩子
    SyncBailHook, // 同步钩子 但是执行过程中有 return值(非undefined)就停止不在执行
    SyncWaterfallHook, // 同步钩子 接受至少一个参数,上一个注册的回调返回值会作为下一个注册的回调的参数。
    SyncLoopHook, // 同步钩子 但是执行过程中有 return(非undefined) 值就会再次执行当前回调函数
    AsyncParallelHook, // 普通异步并行钩子
    AsyncParallelBailHook, // 异步并行钩子但是执行过程中有 return值(非undefined) 直接执行 callAsync 或者 promise 中的函数(由于并行执行的原因,注册的其他回调依然会执行)
    AsyncSeriesHook, // 普通异步串行钩子
    AsyncSeriesBailHook, // 异步串行钩子 会直接执行但是执行过程中有 return值(非undefined ) callAsync 或者 promise 中的函数,并且注册的后续回调都不会执行。
    AsyncSeriesWaterfallHook // 异步串行钩子 上一个注册的异步回调执行之后的返回值会传递给下一个注册的回调。
 } = require("tapable");

 /**
  * tap 注册同步事件 使用tap 方法添加一个消费者
  * tapAsync 注册异步事件写法1
  * tapPromise 注册异步事件写法1
  * 
  * call 调用事件
  * callAsync 调用异步事件
 */

 /******注册同步事件  ******/
// const hooks1 = new SyncHook(['name']); 
// hooks1.tap("hello",(name)=>{
//     console.log(`hello ${name}`);
// })
// hooks1.tap('hello again', (name) => {
//     console.log(`hello ${name}, again`);
// });

// hooks1.call('aaa');
/******注册同步事件  ******/

/******注册异步步事件1  ******/
// const hooks1 = new AsyncParallelHook(['name']); // 最后会自动带一个callback参数
// hooks1.tapAsync("hello",(name,callback)=>{
//     setTimeout(()=>{
//         console.log(`hello ${name}`);
//         callback();
//     },1000)
// })
// hooks1.tapAsync('hello again', (name,callback) => { // 最后会自动带一个callback参数
//     setTimeout(()=>{
//         console.log(`hello ${name}, again`);
//         callback();
//     },2000)
// });

// hooks1.callAsync('aaa',()=>{
//     // 所有的函数执行完毕 end
//     console.log('所有的函数执行完毕 end');
// });

/******注册异步步事件1  ******/

/******注册异步步事件2  ******/
const hooks1 = new AsyncSeriesHook(['name']); // 最后会自动带一个callback参数
hooks1.tapPromise("hello",(name)=>{
    return new Promise(resolve=>{
        setTimeout(()=>{
            console.log(`hello ${name}`);
            resolve();
        },4000)
    })
    
})
hooks1.tapPromise('hello again', (name) => { // 最后会自动带一个callback参数
    return new Promise(resolve=>{
        setTimeout(()=>{
            console.log(`hello ${name}, again`);
            resolve();
        },1000)
    })
});

hooks1.callAsync('aaa',()=>{
    // 所有的函数执行完毕 end
    console.log('所有的函数执行完毕 end');
});

/******注册异步步事件2  ******/

3. 自己写插件

1. webpack.congif.js

const path = require('path');
const MyPlugin = require('./Plugin1');
module.exports = {
    mode: 'production',
    plugins: [
        new MyPlugin()
    ]
};

2. MyPlugin.js

const path = require('path');
const util = require('util');
const fs = require('fs'); // readFile是个异步方法
const webpack = require('webpack');
const {RawSource} = webpack.sources; // 构造函数 作用:将buffer转为webpack格式的数据

// 把readFile 转为 promise 返回一个buffer
const readFile = util.promisify(fs.readFile);



class Plugin1 {
    apply(compiler){ // 参数是编译器 下面写三个 AsyncSeriesHook 串行函数看看
        // 会按照webpack的生命周期去执行对应的函数
        compiler.hooks.emit.tapAsync("plugin1",(compilation,cb)=>{ // compilation实例能够访问所有的模块和它们的依赖(大部分是循环依赖)
            // do sth
            setTimeout(()=>{
                console.log('emit');
                cb();
            },300);
            
        });
        compiler.hooks.afterEmit.tapAsync("plugin1",(compilation,cb)=>{ 
             // do sth
             setTimeout(()=>{
                console.log('afterEmit');
                cb();
            },200);
            
        });
        compiler.hooks.done.tapAsync("plugin1",(stats,cb)=>{ // stats文件信息
             // do sth
             setTimeout(()=>{
                console.log('done');
                cb();
            },100);
           
        });
        // SyncHook
        compiler.hooks.thisCompilation.tap('plugin1',  (compilation)=>{ // thisCompilation 初始化 compilation 时调用
            // compilation 也有很多钩子
            // 为 compilation 创建额外 asset; AsyncSeriesHook
            compilation.hooks.additionalAssets.tapAsync('plugin1', async (cb)=>{
               // console.log(compilation.assets); //只有在这个函数下才能看到静态文件的样子
                // 新增文件方法1 指定对象法 必须包含两个方法 size 和source
                const content = "hello world";
                compilation.assets['a.txt'] = {
                    size(){
                        return content.length;
                    },
                    source(){
                        return content;
                    }
                };
                // 2. 新增文件方法2  使用 buffer 转为webpack数据增加进去 ; 
                // 记得  async await 
                const bData = await readFile(path.resolve(__dirname,'b.txt'));
               compilation.assets['b.txt'] = new RawSource(bData);
                // 3. 新增文件方法3  使用 buffer 转为webpack数据增加进去 ;使用 emitAsset方法 
                // 记得  async await 
                const cData = await readFile(path.resolve(__dirname,'c.txt'));
                compilation.emitAsset('cccc.txt', new RawSource(cData));


                cb();
            })
        })
    }

}
module.exports = Plugin1;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值