# 前言
Webpack
本质上是一种事件流的机制,他的工作流程就是将各个插件串联起来,而实现这一切的核心就是Tapable
。Tapable
的核心原理是依赖于发布订阅模式,它有几个核心的方法,我们简单的来模拟实现下。
# SyncHook: 同步钩子
SyncHook.js 文件
// 同步的钩子
class SyncHook {
constructor(args) {
this.tasks = [];
};
/**
* 注册监听函数
* @param {String} name : 名称
* @param {Function} task : 任务函数
*/
tap(name, task) {
// 这里相当于订阅
this.tasks.push(task);
};
/**
* 执行注册的函数
* @param {...any} args
*/
call(...args) {
this.tasks.forEach(task => task(...args))
}
}
module.exports = { SyncHook };
写一个调用该钩子的模拟学习的模块文件:Lesson.js
Lesson.js 文件
//引入 同步钩子函数
const { SyncHook } = require('./Tapable/SyncHook');
// 模拟学习的课程
class Lesson {
constructor() {
this.hooks = {
// 通过 new 创建实例后会拿到 SyncHook 中的两个实例方法:tap 和 call
test: new SyncHook(['name'])
}
};
// 注册钩子
tap() {
this.hooks.test.tap('Vue', function(name) {
console.log('Vue', name)
})
this.hooks.test.tap('Html', function(name) {
console.log('Html', name)
})
// 这里可以按需写多个
};
// 启动钩子
start() {
this.hooks.test.call('zp');
};
}
let lesson = new Lesson();
lesson.tap(); // 注册钩子 / 事件
lesson.start(); // 启动钩子,会分别输出 Vue zp 和 Html zp
# SyncBailHook:保险钩子
SyncBailHook.js 文件
/**
* 同步的保险钩子
* 任何一个监听函数返回了非undefined的结果就会停止执行
*/
class SyncBailHook {
constructor(args) {
this.tasks = [];
};
/**
* 注册监听函数
* @param {String} name : 名称
* @param {Function} task : 任务函数
*/
tap(name, task) {
// 这里相当于订阅
this.tasks.push(task);
};
/**
* 执行注册的函数
* @param {...any} args
*/
call(...args) {
let ret; // 当前函数的返回值
let index = 0; // 默认先执行第一个
do {
ret = this.tasks[index++](...args);
} while (ret === undefined && index < this.tasks.length)
}
}
module.exports = { SyncBailHook };
同样的Lesson.js
文件,引用的是SyncBailHook.js
文件,如下:
Lesson.js 文件
const { SyncBailHook } = require('./Tapable/SyncBailHook');
// 模拟学习的课程
class Lesson {
constructor() {
this.hooks = {
test: new SyncBailHook(['name'])
}
};
// 注册钩子
tap() {
this.hooks.test.tap('Vue', function(name) {
console.log('Vue', name)
return '芭比Q了'
})
this.hooks.test.tap('Html', function(name) {
console.log('Html', name)
})
};
// 启动钩子
start() {
this.hooks.test.call('zp');
};
}
let lesson = new Lesson();
lesson.tap(); // 注册钩子/事件
lesson.start(); // 启动钩子
因第一个返回了非undefined,所只会输出 Vue zp,且不会执行第二个
# SyncWaterfallHook:瀑布方法
SyncWaterfallHook.js 文件
// 瀑布流式的同步钩子
class SyncWaterfallHook {
constructor(args) {
this.tasks = [];
};
/**
* 注册监听函数
* @param {String} name : 名称
* @param {Function} task : 任务函数
*/
tap(name, task) {
// 这里相当于订阅
this.tasks.push(task);
};
/**
* 执行注册的函数
* @param {...any} args
*/
call(...args) {
let [first, ...others] = this.tasks;
let red = first(...args);
// 迭代执行
others.reduce((pre, curren) => {
return curren(pre);
}, red)
}
}
module.exports = { SyncWaterfallHook };
同样的Lesson.js
文件,引用的是SyncWaterfallHook.js
文件,如下:
Lesson.js 文件
const { SyncWaterfallHook } = require('./Tapable/SyncWaterfallHook');
// 模拟学习的课程
class Lesson {
constructor() {
this.hooks = {
test: new SyncWaterfallHook(['name'])
}
};
// 注册钩子
tap() {
this.hooks.test.tap('Vue', function(name) {
console.log('Vue', name)
return '开始下一个'
})
this.hooks.test.tap('Html', function(data) {
console.log('Html', data)
})
};
// 启动钩子
start() {
this.hooks.test.call('zp');
};
}
let lesson = new Lesson();
lesson.tap(); // 注册钩子/事件
lesson.start(); // 启动钩子
会输出 Vue zp 开始下一个 Html
# SyncLoopHook:循环执行的钩子
SyncLoopHook.js 文件
/**
* 循环执行的钩子
* 遇到某个不返回undefined的监听函数会多次执行
*/
class SyncLoopHook {
constructor(args) {
this.tasks = [];
};
/**
* 注册监听函数
* @param {String} name : 名称
* @param {Function} task : 任务函数
*/
tap(name, task) {
// 这里相当于订阅
this.tasks.push(task);
};
/**
* 执行注册的函数
* @param {...any} args
*/
call(...args) {
this.tasks.forEach(task => {
let ret;
do {
ret = task(...args);
} while (ret != undefined)
})
}
}
module.exports = { SyncLoopHook };
同样的Lesson.js
文件,引用的是SyncLoopHook.js
文件,如下:
Lesson.js 文件
const { SyncLoopHook } = require('./Tapable/SyncLoopHook');
// 模拟学习的课程
class Lesson {
constructor() {
this.hooks = {
test: new SyncLoopHook(['name'])
}
};
// 注册钩子
tap() {
let index = 0;
this.hooks.test.tap('Vue', function(name) {
console.log('Vue', name);
return ++index === 5 ? undefined : 'go on';
})
this.hooks.test.tap('Html', function(data) {
console.log('Html', data)
})
};
// 启动钩子
start() {
this.hooks.test.call('zp');
};
}
let lesson = new Lesson();
lesson.tap(); // 注册钩子/事件
lesson.start(); // 启动钩子
会输出 vue zp vue zp vue zp vue zp vue zp Html zp
几个核心模块的方法到这里就模拟实现完成了,nice ~~