黑客黑网站是做网站,学校网站开发背景,网站导航栏字体,wordpress 微信导航站文章目录 什么是自定义事件总线具体实现思路分析定义结构实现 on实现 emit实现 off 源码 什么是自定义事件总线
自定义事件总线属于一种观察着模式#xff0c;其中包括三个角色发布者#xff08;Publisher#xff09;#xff1a;发出事件#xff08;Event#xff09;订阅… 文章目录 什么是自定义事件总线具体实现思路分析定义结构实现 on实现 emit实现 off 源码 什么是自定义事件总线
自定义事件总线属于一种观察着模式其中包括三个角色发布者Publisher发出事件Event订阅者Subscriber订阅事件Event并且会进行响应Handler事件总线EvnetBus无论是发布者还是订阅者都是通过事件总线作为中台的
具体实现
思路分析 事件总线相信大家都用过或者听过特别是在 vue2 中这就是一种组件传值的方式轻量又简单 从使用上来说通过 on 监听事件同时可以使用 emit 来发送事件当然也可也通过 off 来取消事件 使用还是非常简单的但是怎么才能实现 emit 触发 on 监听的呢竟然要触发的话首先我是不是需要有一个地方可以存储这个通过 on 监听的方法呢存储了之后就是使用emit 实际就是通过参数找到对应的存储方法然后我们帮他手动调用一下 所以基于这一点我们就可以得到三个点on、store、emit如图
定义结构 根据上面的分析我们可以选用 class 的形式来实现 其次我们还需要 on emit off 三个方法如下 class JcEventBus{constructor() {}on(){}emit(){}off(){}
}在这之中我们就按照分析确定一下 store在 class 结构中可以 this 访问数据和方法所以在 JcEventBus 初始化时就可以定义一个变量来存储后续的方法而可以通过一个字符串准确存储和取出的话对象和map都可以但是这里对象会更加好操作所以改造后 constructor 代码如下 constructor() {this.eventBus {}
}实现 on on 方法一般是有两个参数的我们可以写出基础的函数结构如下 on(eventName, callback){}我们不妨在思考一下有时候这个回调函数的this可能需要我们来指定方便在函数内部使用 this所以为了实现这一点一般会给 callback 传递一个普通函数而非匿名函数同时应该有第三个参数来接受这个 this如下 on(eventName, callback, ctx){}确定好参数之后我们来进行一下具体的实现首先假设监听的名称为 foo而且监听的函数有时候可能不止一个而存储多个函数我们可以想到什么数组利用数组来实现存储这些函数需要用的时候在找到对应的函数进行执行即可确定好这一点之后我们要做的就是以 eventName 为 keycallback 存储在数组中为 value{ foo: [fn1,fn2…] }代码如下 on(eventName, callback, ctx){// 如果不存在则初始化为空数组const handles this.eventBus[eventName] || []// 传递一个对象同时保存 执行的函数 和 thishandles.push({ callback, ctx })// 存储在 store 中this.eventBus[eventName] handles
}我们来通过实例化 JcEventBus 来进行测试一下看看是否真的存入了测试代码如下 eventBus.on(foo,function (...payload) {console.log(foo 函数参数为, payload)},obj
)eventBus.on(bar,function (...payload) {console.log(bar 函数参数为, payload)},obj
)console.log(eventBus.eventBus)输出如图
实现 emit emit 要做的事件也非常简单传递事件名称和参数即可因此基础的函数结构如下 emit(eventName, ...payload){}参数可能会有多个使用剩余参数来接收这个方法第一步就是要查找有没有这个方法如果有就执行这个事件名称上绑定的所有函数代码如下 emit(eventName, ...payload) {const handles this.eventBus[eventName]if (!handles || !handles.length) returnhandles.forEach(handle {// 使用 apply 绑定 thishandle.callback.apply(handle.ctx, payload)})
}这个应该非常简单吧直接上测试代码。如下 const eventBus new JcEventBus()const obj { name: coderjc }eventBus.on(foo,function (...payload) {console.log(foo1 函数参数为, payload, this)},obj
)eventBus.on(foo,function (...payload) {console.log(foo2 函数参数为, payload)},obj
)eventBus.emit(foo, 1, 2, 3)结果如图
实现 off off 类似于 removeEventListener 这个方法也是事件名 函数即可取消所以函数基础结构如下 off(eventName, callback){}然后就是通过事件名称找到这个数组在这个数组里面找到对应的函数进行删除即可不过这里会会有一点细节需要注意比如一个函数被存储了多次的情况下当然这个你可以在存储的时候就拦截避免重复但是我们这里没有所以如果删除就要删除多个而这个删除可能有部分的朋友们就会想到遍历全等判断然后删除就行吗这个也可以但是可能需要多一点的操作我们先看看这个思路删除会有什么结果我写了一个demo如下 // 这里用数字代替函数
const arr [1, 2, 2, 4, 5, 6]
const val 2for (let i 0; i arr.length; i) {if (arr[i] val) {// 删除arr.splice(i, 1)}
}
console.log(arr)结果如图 不知道发现了没有只删除了其中的一个2还有一个没有删除这是因为 splice 方法删除之后原数组的长度就-1了此时原来索引为2的数组就会变成索引为1而 i 的值又没有同步的 -1就会直接跳过所以就是这个结果了所以解决方法很简单同时 i-1 即可当然还是有其他方法的都是非常简单的 我这里就直接过滤数组重新赋值了如下 off(eventName, callback){const handles this.eventBus[eventName]if (!handles || !handles.length) returnthis.eventBus[eventName] handles.filter(handle handle.callback ! callback)
}来看看是不是真的有用测试代码如下 const eventBus new JcEventBus()function foo(...payload) {console.log(foo1 函数参数为, payload)
}eventBus.on(foo, foo)eventBus.on(foo, function (...payload) {console.log(foo2 函数参数为, payload)
})// 移除
eventBus.off(foo, foo)eventBus.emit(foo, 1, 2, 3)结果如图
源码 当然了最后我是加了一些对于参数的类型判断非常简单就不单独介绍了 function _verifyType(eventName null, callback null) {if (eventName typeof eventName ! string) {throw new Error(eventName must be a string)}if (callback typeof callback ! function) {throw new Error(callback must be a function)}
}class JcEventBus {constructor() {this.eventBus {}}on(eventName, callback, ctx) {_verifyType(eventName, callback)const handles this.eventBus[eventName] || []handles.push({ callback, ctx })this.eventBus[eventName] handles}off(eventName, callback) {_verifyType(eventName, callback)const handles this.eventBus[eventName]if (!handles || !handles.length) returnthis.eventBus[eventName] handles.filter(handle handle.callback ! callback)}emit(eventName, ...payload) {_verifyType(eventName)const handles this.eventBus[eventName]if (!handles || !handles.length) returnhandles.forEach(handle {handle.callback.apply(handle.ctx, payload)})}
}