React-事件的注册和触发

By
写代码

事件也是React里使用频率很高的操作,各种onClickonFocus/onBluronChangeonSubmit都是经常使用的。事件触发同样是update,也会使用ReactUpdates.batchedUpdates流程,所以会用到前面文章中的内容。

还是以之前的Hello为例,这次给div添加onClick函数事件,函数名为clickFunc。完整代码如下

事件的注册

按上一篇 React-简单组件到浏览器DOM的渲染 的包装层次渲染,最后会调用到最内层的mountComponent函数,流程跟之前讲的是一样的:

判断registrationNameModules.hasOwnProperty(propKey),这里propKeyonClick

enqueuePutListener是事件的关键函数,主要处理流程如下

listenerReg

1、在document注册监听事件

这一步首先为document绑定了click事件,callbackReactEventListener.dispatchEvent

2、存储监听事件

这一步完成之后,要达到的目的是将监听事件存储到listenerBank

listenerBank

listenerBank数据结构

之前说过,ReactReconcileTransaction用到了“池”,即CallBackQueue是用池扩展的,可以调用getPooled/release等一些方法,主要目的是节省开销。用enqueue方法,把callback和对应的context存入Callback_callbacks_contexts。注意,这里用数组下标来对应关系。enqueue的回调函数,会在notifyAll的时候调用,这里会直接在_contexts[x]上调用_callbacks[x]

通过以上步骤,更新了CallbackQueue的属性。

callbackQueue

CallbackQueue结构

notifyAll什么时候调用呢?还是在transaction包装(transactionWrappers)的close函数调用。基本过程如下:

接着会将listener存入listenerBank,这个bank正如其名,存储着所有的listener

3、为node节点添加监听函数

之前的部分都没有涉及到实际的dom节点,在最后的didPutListener函数里,instance获取html节点node,并存入SimpleEventPlugin.onClickListeners:

最后清空CallbackQueue_contexts_callbacks

到这里,事件就注册完成了,总结一下:在document上注册了dispatchEvent这个监听函数,在其他元素上注册了空的监听函数。

事件的触发

事件注册之后,就需要触发。触发的入口是dispatchEvent,关键函数是handleTopLevelImpl,核心是合成事件(SyntheticEvent),描述放在最后。

基本执行流程:

react的所有事件都是在document上通过注册的监听函数(dispatchEvent)下发并触发的。,并且,触发(dispatchEvent)的位置并不是实际渲染出来的DOM元素,而是一个即用即弃的新建元素。

如何知道是在document上触发的监听,而不是其他地方?在之前往document和其他元素绑定事件的时候,都通过EventListener.listen添加,只不过除了document,其他绑定的监听全部是emptyFunction

在前篇 React-函数batchedUpdates和Transaction执行 中介绍过batchedUpdates的执行流程,仍旧是ReactDefaultBatchingStrategy.batchedUpdates的执行,这里不再赘述。特别说明:两个参数分别是回调函数,和该回调接收的参数。

bookKeeping(TopLevelCallbackBookKeeping)结构如下:

bookKeeping

bookKeeping结构

handleTopLevelImpl函数会根据事件的target获取对应的ReactDOM实例。

如何获取呢?之前在渲染的时候,为每个DOM节点都存储了一个internalInstanceKey(形如__reactInternalInstance$xxx),就是为了这里方便反查。如果没有,就向上查父级,直到查到。

handleTopLevel这个函数做了两件事:将原始事件包装为合成事件,然后执行它。

a.包装合成事件

这一步完成之后,要达到的目的是获取到一个synthecitMouseEvent合成事件。

synthetic-mouse-event

合成事件(synthecitMouseEvent)的结构

这里EventConstructor通过继承,最终使用SyntheticEvent的构造函数。

另外获取到的合成事件的同时,会模拟事件捕获和事件冒泡,按照target来补充合成事件的监听函数(_dispatchListeners)和对应ReactDOM实例(_dispatchInstances)。

traverseTwoPhase会按照phasedRegistrationNames(’onClickCapture’(捕获)和’onClick’(冒泡))从listenerBank里取注册的监听函数(clickFunc)。

b.执行事件

这个函数先把事件组合成一个队列eventQueue,然后依次执行。

根据目标DOM节点创建虚拟节点fakeNode = document.createElement('react'),并在其上绑定(addEventListener)并触发(dispatchEvent)事件,最后解除(removeEventListener)。

合成事件SyntheticEvent

合成事件简单来说包装了基础的DOM事件,存储在nativeEvent属性。它包含了DOM事件的接口(包括stopPropagation和preventDefault),不同的是,合成事件是跨平台的。

合成事件是用PooledClass包装的,所以也是会重复利用,也就是说react里的事件是一次性的,一一旦事件执行完,所有属性就会被置null。所以,无法在异步方法(比如setTimeoutsetState等)中获取到触发的合成事件,。

另外,SyntheticEvent使用的是Proxy构造。关于Proxy以后再看。

SyntheticMouseEvent外,还有SyntheticKeyboardEventSyntheticFocusEventSyntheticTouchEvent等合成事件。SyntheticEvent文档 中,有关于onClick等鼠标事件的描述,可以看一下。

1

Comments: 2

  1. 一直用不到就懒得学,现在是学不动…

    04月29日
  2. 文章不错支持一下,非常喜欢

    05月18日

发表评论

电子邮件地址不会被公开。 必填项已用*标注

*

:razz: