React的事件机制

React使用了SyntheticEvent来实现跨浏览器的事件支持,与一般的做法不同的是,React并不直接将事件的监听函数挂在在对应的DOM节点上,而是统一挂载在document上,然后再进行处理。整个过程还是比较复杂的,React的源代码中也以注释的形式进行了整个框架的介绍,如下图。

![alt](/content/images/2017/09/QQ20170926-0-1.png)
整个事件处理,可以分成两部分来讲,一是ReactComponent在创建时如何进行事件绑定;二是,当一个NativeEvent触发以后,如何进行事件传递。后者的流程相对更简单,但需要先把绑定的过程介绍一遍。 # 事件绑定流程 事件绑定的流程还是比较复杂的,先上图,我画了两张图才能把这个流程表示完,~~且图片放大了才能看清 :(~~(点击图片可以查看大图,为了让大图看清楚,特意做了一个插件,https://www.hoyt-tian.com/gei-ghosttian-jia-suo-fang-tu-pian-gong-neng/)
![alt](/content/images/2017/09/ReactEventFlow1.png) ![alt](/content/images/2017/09/ReactEventFlow2.png)
事件绑定也可以拆分成两部分,一部分是绑定TopLevelEvent到document上,另一部分就是,当document上对应的事件触发后,如何将NativeEvent转化成ReactEvent,并与相关的ReactComponent进行关联。完整的处理步骤如下所述:
  • 在准备渲染一个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方法,来处理事件
Show Comments

Get the latest posts delivered right to your inbox.