** 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 | clickHandle = (e) => { | 
** 合成事件机制 **

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中声明的callback,React自身实现了一套事件冒泡机制。
事件存储部分,合成事件以对象池的方式实现创建和销毁,大大提高了性能。