06月28, 2018

tapable笔记

webpack本质上是一种事件流的机制,它的工作流程就是将各个插件串联起来,而实现这一切的核心就是tapable,webpack中最核心的负责编译的Compiler和负责创建bundles的Compilation都是Tapable的实例

Tapable 是一个小型的库,允许你对一个 javascript 模块添加和应用插件。它可以被继承或混入到其他模块中。类似于 NodeJS 的 EventEmitter 类,专注于自定义事件的触发和处理。除此之外,Tapable 还允许你通过回调函数的参数,访问事件的“触发者(emittee)”或“提供者(producer)”。

类似EventEmitter,可似乎比它要强大一些。

tapable用法

const {
    SyncHook,
    SyncBailHook,
    SyncWaterfallHook,
    SyncLoopHook,
    AsyncParallelHook,
    AsyncParallelBailHook,
    AsyncSeriesHook,
    AsyncSeriesBailHook,
    AsyncSeriesWaterfallHook
 } = require("tapable");

alt

Sync*类型的钩子

同步事件。

SyncHook

串行同步执行,不关心返回值

let { SyncHook } = require('tapable');
let queue = new SyncHook(['name']);
queue.tap('1', function (name) {
    console.log(name, 1);
});
queue.tap('2', function (name) {
    console.log(name, 2);
});
queue.tap('3', function (name) {
    console.log(name, 3);
});
queue.call('zpu');

SyncBailHook

串行同步执行,有一个返回值不为null则跳过剩下的逻辑

let { SyncBailHook } = require('tapable');
let queue = new SyncBailHook(['name']);
queue.tap('1', function (name) {
    console.log(name, 1);
});
queue.tap('2', function (name) {
    console.log(name, 2);
    return "Error"
});
queue.tap('3', function (name) {
    console.log(name, 3);
});
queue.call('zpu');

执行结果:

alt

SyncWaterfallHook

看名字就大概能猜出是水流,即会把上一次return的结果作为这一次事件的参数值。

let { SyncWaterfallHook } = require('tapable');
let queue = new SyncWaterfallHook(['name']);
queue.tap('1', function (name) {
    console.log(name, 1);
    return 1;
});
queue.tap('2', function (data) {
    console.log(data, 2);
    return 789;
});
queue.tap('3', function (data) {
    console.log(data, 3);
});
queue.call("123");

执行结果如下:

alt

SyncLoopHook

监听函数返回true表示继续循环,返回undefine表示结束循环

let { SyncLoopHook } = require('tapable');
let queue = new SyncLoopHook(['name']);
let count = 0;
queue.tap('1', function (name) {
    console.log(count++);
    if (count == 3) {
        return;
    } else {
        return true;
    }
});

Async*类型的钩子

异步事件。

  • 支持tap、tapPromise、tapAsync注册
  • 每次都是调用tap、tapSync、tapPromise注册不同类型的插件钩子,通过调用call、callAsync 、promise方式调用。其实调用的时候为了按照一定的执行策略执行,调用compile方法快速编译出一个方法来执行这些插件。

Async可以分为并行和串行,即ParallelSeries,两者的区别就是是在执行的时间上。

并行

  • AsyncParallelHook
  • AsyncParallelBailHook

tapAsync

let { AsyncParallelHook } = require('tapable');
let queue = new AsyncParallelHook(['name']);
console.time('cost');
queue.tapAsync('1', function (name, callback) {
    setTimeout(function () {
        console.log(1);
        callback();
    }, 1000)
});
queue.tapAsync('2', function (name, callback) {
    setTimeout(function () {
        console.log(2);
        callback();
    }, 2000)
});
queue.tapAsync('3', function (name, callback) {
    setTimeout(function () {
        console.log(3);
        callback();
    }, 3000)
});
queue.callAsync('zpu', err => {
    console.log(err);
    console.timeEnd('cost');
});

tapPromise

let { AsyncParallelHook } = require('tapable');
let queue = new AsyncParallelHook(['name']);
console.time('cost');
queue.tapPromise('1', function (name, callback) {
    return new Promise(function (resolve, reject) {
        setTimeout(function () {
            console.log(1);
            resolve();
        }, 1000)
    });

});
queue.tapPromise('2', function (name, callback) {
    return new Promise(function (resolve, reject) {
        setTimeout(function () {
            console.log(2);
            resolve();
        }, 2000)
    });
});
queue.tapPromise('3', function (name, callback) {
    return new Promise(function (resolve, reject) {
        setTimeout(function () {
            console.log(3);
            resolve();
        }, 3000)
    });
});
queue.promise('zpu').then(() => {
    console.timeEnd('cost');
})

看了一下例子,其实就写法上的区别,理解起来还是比较容易的。

AsyncParallelBailHook异步的场景是这样的:

let {AsyncParallelBailHook} = require('tapable');
let queue = new AsyncParallelBailHook(['name']);
console.time('cost');
queue.tapAsync('1',function(name, callback){
    console.log(1);
    callback();
});
queue.tapAsync('2',function(name, callback){
    console.log(2);
    callback("22");
});
queue.tapAsync('3',function(name, callback){
    console.log(3);
    callback();
});
queue.callAsync('zfpx',err=>{
    console.log(err);
    console.timeEnd('cost');
});

当callback的参数不为null时,就不会再执行下面的流程了。

串行

  • AsyncSeriesHook
  • AsyncSeriesBailHook
  • AsyncSeriesWaterfallHook

前两个的tap/tapAsync/tapPromise,使用和上面的写法基本差不多。

这里说一下AsyncSeriesWaterfallHook的tap这三个吧。

tap

let { AsyncSeriesWaterfallHook } = require('tapable');
let queue = new AsyncSeriesWaterfallHook(['name']);
console.time('cost');
queue.tap('1', function (name, callback) {
    console.log(1, name);
    return "88";
});
queue.tap('2', function (data) {
    console.log(2, data);
    return 66;
});
queue.tap('3', function (data) {
    console.log(3, data);
});
queue.callAsync('zpu', err => {
    console.log(err);
    console.timeEnd('cost');
});

alt

tapAsync

let { AsyncSeriesWaterfallHook } = require('tapable');
let queue = new AsyncSeriesWaterfallHook(['name']);
console.time('cost');
queue.tapAsync('1', function (name, callback) {
    setTimeout(function () {
        console.log(name);
        callback(null, 1);
    }, 1000)
});
queue.tapAsync('2', function (data, callback) {
    setTimeout(function () {
        console.log(data);
        callback(null, 2);
    }, 2000)
});
queue.tapAsync('3', function (data, callback) {
    setTimeout(function () {
        console.log(data);
        callback(null, 3);
    }, 3000)
});
queue.callAsync('zpu', err => {
    console.log(err);
    console.timeEnd('cost');
});

当callback第一个参数不为null时,那么下面的注册事件也不会再执行。

tapPromise

let { AsyncSeriesHook } = require('tapable');
let queue = new AsyncSeriesHook(['name']);
console.time('cost');
queue.tapPromise('1', function (name) {
    return new Promise(function (resolve, reject) {
        setTimeout(function () {
            console.log(1);
            //resolve();
            reject();
        }, 1000)
    });
});
queue.tapPromise('2', function (name, callback) {
    return new Promise(function (resolve) {
        setTimeout(function () {
            console.log(2);
            resolve();
        }, 2000)
    });
});
queue.tapPromise('3', function (name, callback) {
    return new Promise(function (resolve) {
        setTimeout(function () {
            console.log(3);
            resolve();
        }, 3000)
    });
});
queue.promise('zpu').then(err => {
    console.log(err);
    console.timeEnd('cost');
}, err => {
    console.log(err);
    console.timeEnd('cost');
});

在webpack中的使用

alt

在写webpack插件时:

compiler.hooks.emit.tapAsync('EmitPlugin', (compilation, callback) => {
    // 处理逻辑
    callback();
});

挂载上方法,然后webpack会帮我们去执行。

总结

这个tapable的使用还是比较简单的。基本上会tap、tapAsync,差不多就OK了。

本文链接:www.my-fe.pub/post/tapable-note.html

-- EOF --

Comments

评论加载中...

注:如果长时间无法加载,请针对 disq.us | disquscdn.com | disqus.com 启用代理。