发布订阅者模式
TIP
什么是发布订阅者模式?简单说发布订阅模式就是 监听on 派发emit该函数的回调
vueBus
TIP
使用过vueBus 的就了解 on绑定 和使用 emit触发该vueBus;
大致代码就是:
js
const vueBus = new Vue();
//使用$on 绑定blog
vueBus.$on('blog',() => {
console.log('我是三原')
});
//使用$emit 触发blog 绑定的回调
vueBus.$emit('blog')
//解除绑定就是使用 $off
vueBus.$off('blog')
//使用once 绑定 触发一次就解除绑定
vueBus.$once('blog-once',() => {
console.log('我是三原once')
});
WARNING
大致可以分为 4个方法 分别是: on、emit、once、off
,那接下来咱们就实现自己的发布、订阅模式吧。
Start
- 先定义一个咱们的函数 EventEmitter
- events 存放咱们的订阅的方法
js
class EventEmitter {
constructor() {
this.events = {}
}
}
on
- on 函数确定参数(2个)
- 参数1 String
- 参数2 function 回调函数
- 把绑定的值放在 events 对象里面
{[kay: String(参数1)]: [function(参数2)] }
- 值定义成数组的初衷是能对一个key再不同的地方绑定多次
- 功能: 绑定事件
js
class EventEmitter {
constructor() {
this.events = {}
}
/*
*@dec 接收两个参数
*@params {String} eventName
*@params {Function} cb
*/
on(eventName, cb) {
//把绑定的放在events里面就是
if(this.events[eventName]) {
//绑定过的就放里面push就行了
this.events[eventName].push(cb)
} else {
//没有绑定过的直接定义为数组类型,并把该function 放在数组里面
this.events[eventName] = [cb]
}
}
}
emit
- 确定参数2个
- 参数1 String 也就是对应绑定的on的key
- 参数2 传参 也就是咱们emit的时候带的参数
- 功能:触发对应key的 回调cb
js
class EventEmitter {
constructor() {
this.events = {}
}
/*
*@dec 接收两个参数
*@params {String} eventName
*@params {Function} cb
*/
on(eventName, cb) {
//把绑定的放在events里面就是
if(this.events[eventName]) {
//绑定过的就放里面push就行了
this.events[eventName].push(cb)
} else {
//没有绑定过的直接定义为数组类型,并把该function 放在数组里面
this.events[eventName] = [cb]
}
}
emit(eventName, ...args) {
//this.events[eventName] 这个时候能看到就是咱们on 绑定的[] { key: []}
//forEach 循环调用
//参数args
this.events[eventName] && this.events[eventName].forEach(cb => {
cb(...args);
});
}
}
验证on、emit
js
//new 一个咱们刚写的函数类
const myEvent = new EventEmitter();
//绑定
myEvent.on('blog-event',(params) => {
console.log(params, '三原社区')
});
//触发
myEvent.emit('blog-event', '我是三原社区的一员,这是三原自定义的发布订、阅者模式')
//输出:我是三原社区的一员,这是三原自定义的发布订、阅者模式 三原社区
on、emit验证通过
- 验证通过 输出 我是三原社区的一员,这是三原自定义的发布订、阅者模式 三原社区
- 接下来咱们再写off 解除绑定事件
off
- 确定参数1个
- 参数1 就是咱们要解除绑定的key
- 功能: 解除绑定、释放绑定
js
class EventEmitter {
constructor() {
this.events = {}
}
/*
*@dec 接收两个参数
*@params {String} eventName
*@params {Function} cb
*/
on(eventName, cb) {
//把绑定的放在events里面就是
if(this.events[eventName]) {
//绑定过的就放里面push就行了
this.events[eventName].push(cb)
} else {
//没有绑定过的直接定义为数组类型,并把该function 放在数组里面
this.events[eventName] = [cb]
}
}
emit(eventName, ...args) {
//this.events[eventName] 这个时候能看到就是咱们on 绑定的[] { key: []}
//forEach 循环调用
//参数args
this.events[eventName] && this.events[eventName].forEach(cb => {
cb(...args);
});
}
off(eventName) {
if(this.events[eventName]) {
delete this.events[eventName]
}
}
}
off验证
js
myEvent.on('off-test',(params) => {
console.log(params, '三原社区')
});
myEvent.emit('off-test','我是未解除绑定的off-test');
myEvent.off('off-test');
myEvent.emit('off-test','我是解除绑定的off');
//输出:我是未解除绑定的off-test
off 验证通过
- off验证通过
once
- 确定参数2个 跟on函数一样
- 功能: 只能调用一次就解除绑定;
咱们试着写写看如下
js
class EventEmitter {
constructor() {
this.events = {}
}
/*
*@dec 接收两个参数
*@params {String} eventName
*@params {Function} cb
*/
on(eventName, cb) {
//把绑定的放在events里面就是
if(this.events[eventName]) {
//绑定过的就放里面push就行了
this.events[eventName].push(cb)
} else {
//没有绑定过的直接定义为数组类型,并把该function 放在数组里面
this.events[eventName] = [cb]
}
}
emit(eventName, ...args) {
//this.events[eventName] 这个时候能看到就是咱们on 绑定的[] { key: []}
//forEach 循环调用
//参数args
this.events[eventName] && this.events[eventName].forEach(cb => {
cb(...args);
});
}
off(eventName) {
if(this.events[eventName]) {
delete this.events[eventName]
}
}
once(eventName, cb) {
//只能触发一次就立即解除绑定那
//自己定义一个函数
//函数包括: 调用cb回调、解除绑定
const once = (...args) => {
cb(...args)
this.off(eventName)
}
//绑定
this.on(eventName, once)
}
}
once验证
js
myEvent.once('once-test',(params) => {
console.log(params, '三原社区')
});
myEvent.emit('once-test','我是第一次调用的once-test');
myEvent.emit('once-test','我是第二次调用的once-test');
//输出: 我是第一次调用的once-test 三原社区
once验证通过
- 验证通过
实用场景
- 可以在你写插件的时候,帮你和用插件的人实现交互
- 你只需要继承一下该函数就成了(用法)
js
class CustomClass extends events {
constructor() {
super()
}
init() {
this.emit('extends-events', '我是使用发布订阅者的插件哦~~~~~~')
}
}
const customClass = new CustomClass();
//绑定
customClass.on('extends-events',(params) => {
console.log(params, '三原社区')
})
customClass.init();
// 输出:我是使用发布订阅者的插件哦~~~~~~ 三原社区
结束语
TIP
当然社区也有好多比较好的npm
包可以直接用,比如:eventemitter2...
。 宗旨在会的基础上去使用
- TS 版本的有需要可以直接copy
ts
type callBack = (arg?: any) => void
class EventEmitter {
_events: { [key: string]: any };
constructor() {
this._events = {}
}
public on(eventName: string, cb: callBack): void {
if (this._events[eventName]) {
this._events[eventName].push(cb);
} else {
this._events[eventName] = [cb];
}
}
public emit(eventName: string, ...args: any): void {
this._events[eventName] && this._events[eventName].forEach((fn: callBack) => {
fn(...args);
});
}
public off(eventName: string, cb: callBack): void {
if (this._events && this._events[eventName]) {
delete this._events[eventName]
}
}
public once(eventName: string, cb: callBack): void {
const one = () => {
cb();
this.off(eventName, one);
};
this.on(eventName, one);
}
}
export default EventEmitter;
- 基于
eventemitter2
封装
ts
import { EventEmitter2, event, ListenerFn } from 'eventemitter2';
const MAX_LISTENERS = 9999;
class EventEmitter {
private eventEmitter: EventEmitter2 = new EventEmitter2();
public emit = this.eventEmitter.emit.bind(this.eventEmitter);
public on = this.eventEmitter.on.bind(this.eventEmitter);
public once = this.eventEmitter.once.bind(this.eventEmitter);
constructor() {
this.eventEmitter.setMaxListeners(MAX_LISTENERS);
}
public off(eventName: event, listener?: ListenerFn) {
if (listener !== undefined) {
return this.eventEmitter.off(eventName, listener);
} else {
return this.eventEmitter.removeAllListeners(eventName);
}
}
}
export { EventEmitter };
export default new EventEmitter();