react合成事件

Preface

最近在代码中遇到了react合成事件的相关问题,本文做下记录。


合成事件(SyntheticEvent)

在react中,事件处理程序将传递 SyntheticEvent 的实例,这是一个跨浏览器原生事件包装器。 它具有与浏览器原生事件相同的接口,包括 stopPropagation()preventDefault() ,除了事件在所有浏览器中他们工作方式都相同。

如果您发现由于某种原因需要底层浏览器事件,只需使用 nativeEvent 属性来获取它。 每个 SyntheticEvent对象都具有以下属性:

类型 属性
boolean bubbles
boolean cancelable
DOMEventTarget currentTarget
boolean defaultPrevented
number eventPhase
boolean isTrusted
DOMEvent nativeEvent
void preventDefault()
boolean isDefaultPrevented()
void stopPropagation()
boolean isPropagationStopped()
DOMEventTarget target
number timeStamp
string type

异步事件

SyntheticEvent对象是通过合并得到的。这意味着在事件回调被调用后,SyntheticEvent对象将被重用并且所有属性都将被取消。 这是出于性能原因。 因此,您无法以异步方式访问该事件。
如果要以异步方式访问事件属性,应该对事件调用 event.persist() ,这将从池中删除合成事件,并允许用户代码保留对事件的引用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
clickHandle = (e) => {
# 这一行必须不可少
e.persist();
console.log(e);
console.log(e.type);
# 这里是异步的
setTimeout(() => {
console.log(e);
console.log(e.type);
}, 100);

# 这里也是异步的
this.setState({event:event})
}

合成事件机制

syntheticEvent.png

react的所有事件都挂载在document中。当真实dom触发后冒泡到document后才会对react事件进行处理,所以原生的事件会先执行,然后执行react合成事件,最后执行真正在document上挂载的事件。

react事件机制分为三个部分:

事件注册部分,所有的事件都会注册到document上,拥有统一的调函数dispatchEvent来执行事件分发。React使用对象池来管理合成事件对象的创建和销毁,这样减少了垃圾的生成和新对象内存的分配,大大提高了性能。也就是说不同的事件,可能会共享一个合成事件对象。

触发document注册原生事件的回调dispatchEvent,获取到触发这个事件最深一级的元素,遍历这个元素的所有父元素,依次对每一级元素进行处理。构造合成事件。将每一级的合成事件存储在eventQueue事件队列中。遍历eventQueue。通过isPropagationStopped判断当前事件是否执行了阻止冒泡方法。如果阻止了冒泡,停止遍历,否则通过executeDispatch执行合成事件。释放处理完成的事件。

事件分发部分,首先生成合成事件,注意同一种事件类型只能生成一个合成事件Event,如onclick这个类型的事件,dom上所有带有通过jsx绑定的onClick的回调函数都会按顺序(冒泡或者捕获)会放到Event._dispatchListeners 这个数组里,后面依次执行它。也就是说,React以队列的方式,从触发事件的组件向父组件回溯,调用它们在JSX中声明的callbackReact自身实现了一套事件冒泡机制。

事件存储部分,合成事件以对象池的方式实现创建和销毁,大大提高了性能。


参考链接

关注我的微信公众号[李一二],即时看更多的文章