分析webpack中使用的Tapable——同步鉤子實現及模擬實現

tapable

webpack本質上是一種事件流的機制,他的工作流程就是將各個插件串聯起來,而實現這一切的核心就是tapable,

tapable有點類似於nodejs的events庫,核心原理也是依賴於發佈訂閱模式:

繼承了很多插件,同步的和異步的

tapable庫中有3中註冊方法 tap(同步註冊)、tabAsync(回調cb)、tabPromise(註冊是promise)

調用的三種方法 call、 callAsync、 promise

SyncHook

作用: 讓添加的鉤子按順序執行
使用方式:

let {SyncHook} = require('tapable');

class Lesson {
    constructor() {
        this.hooks = {
            arch: new SyncHook(['name']),
        }
    }
    // 註冊監聽函數
    tap() {
        this.hooks.arch.tap('node', function(name) {
            console.log('node', name);
        });
        this.hooks.arch.tap('react', function(name) {
            console.log('react', name);
        });
    }
    start() {
        this.hooks.arch.call('zhangfeng');
    }
}

let les = new Lesson();

les.tap(); // 註冊這兩個事件
les.start(); // 啓動鉤子

實現原理:

class SyncHook {    // 鉤子是同步的
    constructor(args) { // args => ['name']
        this.tasks = [];
    }
    call(...args) {
        this.tasks.forEach(task => task(...args));
    }
    tap(name, task) {
        this.tasks.push(task);
    }
}

let hook = new SyncHook(['name']);

hook.tap('react', function (name) {
    console.log('react', name);
});

hook.tap('node', function(name) {
    console.log('node', name);
})

hook.call('zhangfeng');

SyncBail

作用: 如果有一個鉤子返回的不是undefined,則不再往下執行;
使用方式:

let {SyncBailHook} = require('tapable');

class Lesson {
    constructor() {
        this.hooks = {
            arch: new SyncBailHook(['name']),
        }
    }
    // 註冊監聽函數
    tap() {
        this.hooks.arch.tap('node', function(name) {
            console.log('node', name);
            return 'node還需要加強學習'
        });
        this.hooks.arch.tap('react', function(name) {
            console.log('react', name);
        });
    }
    start() {
        this.hooks.arch.call('zhangfeng');
    }
}

let les = new Lesson();

les.tap(); // 註冊這兩個事件
les.start(); // 啓動鉤子

實現原理:

class SyncBailHook {    // 鉤子是同步的
    constructor(args) { // args => ['name']
        this.tasks = [];
    }
    call(...args) {
        // this.tasks.forEach(task => task(...args));
        let ret;    // 這個函數的返回值
        let index = 0;  // 當前要先執行第一個
        do {
            ret = this.tasks[index++](...args);
        }while (ret === undefined && index < this.tasks.length);
    }
    tap(name, task) {
        this.tasks.push(task);
    }
}

let hook = new SyncBailHook(['name']);

hook.tap('react', function (name) {
    console.log('react', name);
    return '到此爲止'
});

hook.tap('node', function(name) {
    console.log('node', name);
});

hook.call('zhangfeng');

SyncWaterfallHook

作用: 將上一個鉤子的返回值,作爲下一個鉤子的參數
使用方法:

let {SyncWaterfallHook} = require('tapable');
// waterfall 瀑布
class Lesson {
    constructor() {
        this.hooks = {
            arch: new SyncWaterfallHook(['name']),
        }
    }
    // 註冊監聽函數
    tap() {
        this.hooks.arch.tap('node', function(name) {
            console.log('node', name);
            return 'node還需要加強學習'
        });
        this.hooks.arch.tap('react', function(data) {
            console.log('react', data);
        });
    }
    start() {
        this.hooks.arch.call('zhangfeng');
    }
}

let les = new Lesson();

les.tap(); // 註冊這兩個事件
les.start(); // 啓動鉤子

實現原理:

class SyncWaterfallHook {    // 鉤子是同步的
    constructor(args) { // args => ['name']
        this.tasks = [];
    }
    call(...args) {
        let [first, ...others] = this.tasks;
        let ret = first(...args);
        others.reduce((a, b) => {
            return b(a);
        }, ret);
    }
    tap(name, task) {
        this.tasks.push(task);
    }
}

let hook = new SyncWaterfallHook(['name']);

hook.tap('react', function (name) {
    console.log('react', name);
    return '到此爲止'
});

hook.tap('node', function(data) {
    console.log('node', data);
});

hook.call('zhangfeng');

SyncLoopHook

作用:如果一個鉤子返回不是undefined,則繼續執行該鉤子
使用方法:

let {SyncLoopHook} = require('tapable');
// 遇到某個不返回undefined的監聽函數,會多次執行
class Lesson {
    constructor() {
        this.index = 0;
        this.hooks = {
            arch: new SyncLoopHook(['name']),
        }
    }
    // 註冊監聽函數
    tap() {
        this.hooks.arch.tap('node', (name) => {
            console.log('node', name);
            return ++this.index === 3 ? undefined : 'node還需要加強學習';
        });
        this.hooks.arch.tap('react', (name) => {
            console.log('react', name);
        });
    }
    start() {
        this.hooks.arch.call('zhangfeng');
    }
}

let les = new Lesson();

les.tap(); // 註冊這兩個事件
les.start(); // 啓動鉤子

實現原理:

class SyncLoopHook {    // 鉤子是同步的
    constructor(args) { // args => ['name']
        this.tasks = [];
    }
    call(...args) {
        this.tasks.forEach(task => {
            let ret;
            do {
                ret = task(...args);
            } while (ret!==undefined);
        })
    }
    tap(name, task) {
        this.tasks.push(task);
    }
}

let hook = new SyncLoopHook(['name']);
let total = 0;
hook.tap('react', function (name) {
    console.log('react', name);
    return ++total === 3 ? undefined : '繼續學習react';
});

hook.tap('node', function(name) {
    console.log('node', name);
});

hook.call('zhangfeng');

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章