热身准备
明确几个概念
在[email protected]
版本中:
- 所有事件都是委托在
id = root
的DOM元素中(网上很多说是在document
中,17
版本不是了); - 在应用中所有节点的事件监听其实都是在
id = root
的DOM元素中触发; React
自身实现了一套事件冒泡捕获机制;React
实现了合成事件SyntheticEvent
;React
在17
版本不再使用事件池了(网上很多说使用了对象池来管理合成事件对象的创建销毁,那是16
版本及之前);- 事件一旦在
id = root
的DOM元素中委托,其实是一直在触发的,只是没有绑定对应的回调函数;
盗用一张官方图,按官方解释,之所以会将事件委托从document
中移到id = root
的DOM元素,是为了可以更加安全地进行新旧版本 React 树的嵌套。
感兴趣的可以访问:React中文网站 。
事件系统角色划分
- 事件注册:
registerEvents
; - 事件监听:
listenToAllSupportedEvents
; - 事件合成:
SyntheticBaseEvent
; - 事件派发:
dispatchEvent
;
事件注册
事件注册是自执行的,也就是React
自身进行调用的:
// 注册React事件
registerSimpleEvents();
registerEvents$2();
registerEvents$1();
registerEvents$3();
registerEvents();
React
事件就是在组件中调用的onClick
这种写法的事件。上面分为5个函数写,主要是区分不同的事件注册逻辑,但是最后都会添加到allNativeEvents
的Set
数据结构中。
registerSimpleEvents
这里会注册大部分事件,它们在React
被定义为顶级事件。
它们分为三类:
- 离散事件:
discreteEvent
,常见的如:click, keyup, change
; - 用户阻塞事件:
userBlocking
,常见的如:dragEnter, mouseMove, scroll
; - 连续事件:
continuous
,常见的如:error, progress, load,
;
它们的优先级排序:
0:离散事件, 1:用户阻塞事件, 2:连续事件
它们会注册冒泡和捕获阶段两个事件。
registerEvents$2
注册类似onMouseEnter
,onMouseLeave
单阶段事件,只注册冒泡阶段事件。
registerEvents$1
注册onChange
相关事件,注册冒泡和捕获阶段两个事件。
registerEvents$3
注册onSelect
相关事件,注册冒泡和捕获阶段两个事件。
registerEvents
注册onBeforeInput
,onCompositionUpdate
等相关事件,注册冒泡和捕获阶段两个事件。
事件监听
在React源码系列之二:React的渲染机制曾提到过,React
在开始渲染前,会为应用创建一个fiberRoot
作为应用的根节点。在创建fiberRoot
还会做一件事,就是
listenToAllSupportedEvents(rootContainerElement);
从字面就能理解这个函数是做事件监听的,其中rootContainerElement
参数就是应用中的id = root
的DOM元素。相关参考视频讲解:进入学习
该函数主要遍历上面事件注册添加到allNativeEvents
的事件,按照一定规则,区分冒泡阶段,捕获阶段,区分有无副作用进行监听,监听的api还是addEventListener
:
// 监听冒泡阶段事件
function addEventBubbleListener(target, eventType, listener) {
target.addEventListener(eventType, listener, false);
return listener;
}
// 监听捕获阶段事件
function addEventCaptureListener(target, eventType, listener