import Taro, { Events } from '@tarojs/taro'
const events = new Events()
// 监听一个事件,接受参数
events.on('eventName', (arg) => {
// doSth
})
// 监听同个事件,同时绑定多个 handler
events.on('eventName', handler1)
events.on('eventName', handler2)
events.on('eventName', handler3)
// 触发一个事件,传参
events.trigger('eventName', arg)
// 触发事件,传入多个参数
events.trigger('eventName', arg1, arg2, ...)
// 取消监听一个事件
events.off('eventName')
// 取消监听一个事件某个 handler
events.off('eventName', handler1)
// 取消监听全部事件
events.off()
复制代码
大体看下源码,有一个总体观,切记一上来就一股脑钻进某个细节里。javascript
从源码里,能够大概知道,做者是经过class
面向对象的方式实现,内部管理一个callbacks
对象,注册一个事件,往对象里添加一个事件属性对象,事件属性对象下有几个属性,一个是事件回调函数callback
,一个是执行上下文context
,还有一个next
, 做用是多个callback
对象嵌套。java
触发事件时,经过事件名,找到对应的对象,完成回调函数的执行。node
取消事件时,根据取消事件的名,找到对应的对象,删掉。 若是没有传事件名, callbacks
所有删除。git
上面的分析是一个事件监听器最基本的功能,如今分析一下其余的状况和功能github
问题1: 注册了一个事件,绑定一个回调函数,若是针对这个事件绑定多个回调函数,怎么办,并且在这种状况下触发顺序是要根据绑定前后顺序依次执行,怎么能保证这个顺序问题。bash
# 解决思路
// 第一次注册onClick事件, 回调函数handle1, callbacks对象是这样的:
{
onClick: {
next: {
callback: handle1,
context: undefined,
next: {}
}
}
}
// 第二次:
{
onClick: {
next: {
callback: handle1,
context: undefined,
next: {
callback: handle2,
context: undefined,
next: {}
}
}
}
}
// 以此类推...
当在触发的时候, 根据这个对象,依次从外往里层层执行
思路和方向是这样的,具体怎么实现,后面看源码就一目了然
复制代码
问题2: 只监听一次的需求怎么实现,意思是,注册一个事件,而后一旦触发,就取消掉这个事件的监听,再没机会触发。app
# 解决思路
注册一个事件,绑定一个对应的函数。
一样的思路,你注册一个事件,完成一个回调任务A, 在内部我保证触发的时候完成你的回调任务A的同时,我多作一件事(取消), 即从新包装一个新的回调给到注册函数
const wrapper = (...args) => {
callback.apply(this, args)
this.off(events, wrapper, context)
}
this.on(events, wrapper, context)
复制代码
问题3: 取消事件的时候,若是我要实现只是取消一个事件里某个回调handle
,意思是,一个事件可能绑定handle1
、handle2
, 如今作到触发时不要执行handle2
, 怎么思路?函数
# 解决思路
若是是这种状况,能够把handle2忽略掉,把关注点放在剩下的handle1,即把这些剩下的,没取消的handle都从新的注册一遍
// 这个判断做用,过滤被取消的handle, 其余的从新注册一遍
if ((callback && cb !== callback) || (context && ctx !== context)) {
this.on(event, cb, ctx)
}
复制代码
理解完上面的思路,对下面的源码,看起来就很顺畅。 思路同样,实现方式能够有多种,只是做者的写法也很值得学习。学习
class Events {
constructor (opts) {
if (typeof opts !== 'undefined' && opts.callbacks) {
this.callbacks = opts.callbacks
} else {
this.callbacks = {}
}
}
on (events, callback, context) {
let calls, event, node, tail, list
if (!callback) {
return this
}
events = events.split(Events.eventSplitter)
calls = this.callbacks
while ((event = events.shift())) {
list = calls[event]
node = list ? list.tail : {}
node.next = tail = {}
node.context = context
node.callback = callback
calls[event] = {
tail,
next: list ? list.next : node
}
}
return this
}
once (events, callback, context) {
const wrapper = (...args) => {
callback.apply(this, args)
this.off(events, wrapper, context)
}
this.on(events, wrapper, context)
return this
}
off (events, callback, context) {
let event, calls, node, tail, cb, ctx
if (!(calls = this.callbacks)) {
return this
}
if (!(events || callback || context)) {
delete this.callbacks
return this
}
events = events ? events.split(Events.eventSplitter) : Object.keys(calls)
while ((event = events.shift())) {
node = calls[event]
delete calls[event]
if (!node || !(callback || context)) {
continue
}
tail = node.tail
while ((node = node.next) !== tail) {
cb = node.callback
ctx = node.context
if ((callback && cb !== callback) || (context && ctx !== context)) {
this.on(event, cb, ctx)
}
}
}
return this
}
trigger (events) {
let event, node, calls, tail, rest
if (!(calls = this.callbacks)) {
return this
}
events = events.split(Events.eventSplitter)
rest = [].slice.call(arguments, 1)
while ((event = events.shift())) {
if ((node = calls[event])) {
tail = node.tail
while ((node = node.next) !== tail) {
node.callback.apply(node.context || this, rest)
}
}
}
return this
}
}
Events.eventSplitter = /\s+/
export default Events
复制代码
Events.eventSplitter = /\s+/
、 events = events.split(Events.eventSplitter)
这个的做用是, 若是事件名是'onClick onTouch'
这样的状况, 是可以对2种自定义事件完成注册