欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

webpack学习5-手写loader

程序员文章站 2024-02-11 11:41:28
...

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;
相关标签: webpack