React使用了SyntheticEvent来实现跨浏览器的事件支持,与一般的做法不同的是,React并不直接将事件的监听函数挂在在对应的DOM节点上,而是统一挂载在document上,然后再进行处理。整个过程还是比较复杂的,React的源代码中也以注释的形式进行了整个框架的介绍,如下图。
- 在准备渲染一个React组件时(ReactMount.renderComponent),会调用ReactMount.prepareTopLevel
- 如果没有开启监听,则调用ReactEvent中的ensureListening,给document添加相关的事件监听
- 具体到每一种事件监听的添加,会通过ReactEvent.trapBubbledEvent进行处理
- 传给trapBubbledEvent的回调函数,由ReactEvent.TopLevelCallbackCreator.createTopLevelCallback(topLevelType)生成
- trapBubbledEvent中,直接调用了NormalizedEventListener.listen方法,这个方法会给回调函数增加一个wrapper函数,wrapper函数做的事情,就是确保事件对象有一个正确的target属性
- 而在createTopLevelCallback时,返回了一个函数,处理修正后的event对象,首先通过event.target查询到对应的ReactComponent对象和相应的DOM节点,然后开始准备处理事件(ReactEvent.handleTopLevel)
- 在ReactEvent.handleTopLevel时,首先根据事件类型、目标节点、相关ReactComponent对象生成一些AbstractEvent,然后将这些事件添加到EventPluginHub的等待队列中,最后处理事件队列
- 在EventPluginHub.extractAbstractEvents生成abstractEvents时,会遍历所有的插件,符合条件的插件,会通过调用extractAbstractEvents来具体生成相应的AbstractEvent
- 以SimpleEventPlugin为例,这个事件插件主要用来为基本的浏览器事件生成回调,并且根据需要修正事件某些属性的值。当条件符合时,该插件会直接从AbstractEvent的缓冲池中取出一个可用的实例,给它重新赋值,并为之生成相应的_dispatches,生成的具体代码在EventPropagators.accumulateTwoPhaseDispatches中
- 在accumulateTwoPhaseDispatches中,每一个abstractEvent实际上都会被执行accumulateTwoPhaseDispatchesSingle,而accumulateTwoPhaseDispatchesSingle实际上是添加Capture和Bubble的dispatches
- 在执行accumulateDirectionalDispatches时,会根据当前的PropagationPhases决定添加Bubble还是Capture,统一通过listenerAtPhase来创建listener。所有生成的listener,都会被添加到abstractEvent._dispatchListeners中
- ListenerAtPhase中,会通过getListener方法根据domId查询定义ReactComponent时的事件回调函数,而getListener则是在CallbackRegistry中定义
事件触发流程
React中的事件触发流程,基于前面的步骤描述,就会简单一些。
- 首先,document的监听函数捕获到对应的事件
- 这个事件会被NormalizedEventListener中的normalizeEvent方法处理,添加target属性
- 随后,被处理过的事件,会被ReactEvent.handleTopLevel处理
- 在ReactEvent.handleTopLevel中,会根据topLevelType,nativeEvent等信息,生成对应的abstractEvents
- 生成的abstractEvents会被添加到EventPluginHub的等待队列中
- EventPluginHub.processAbstractEventQueue将队列中的AbstractEvent全部处理掉
- 处理时,调用的时EventPluginHub中的executeDispatchesAndRelease
- 根据AbstractEvent,获取对应的Plugin,调用Plugin的executeDispatch方法,来处理事件