零、从零开始

在看下面 React 事件系统相关流程之前,我们先看看整个 React 渲染的大概流程;以便于后面更轻松地学习 React 事件系统相关概念。
下图大概是描述了不管 Mount 还是 Update 触发的行为到完成渲染,都会经历 Render 和 Commit 两个阶段,Render 阶段做的事情:根据优先级调度执行过程可打断、遍历Fiber树、Diff更新Fiber树等,Commit 阶段触发生命周期钩子,遍历Fiber完成渲染。

一、浏览器事件流

DOM 事件流的三个阶段

事件捕获阶段
目标元素阶段
事件冒泡阶段

示意图

二、React 事件系统

合成事件的说明 React 并不会在该 DOM 元素上直接绑定事件处理器,React 内部自定义了一套事件系统,在这个系统上统一进行事件订阅和分发。
具体来讲,React 利用事件委托机制在 Root 容器上统一监听 DOM 事件,再根据触发的 target 将事件分发到具体的组件实例。另外上面 e 是一个合成事件对象( SyntheticEvent ), 而不是原始的 DOM 事件对象。

为何要自定义事件系统

根据 W3C 规范定义合成事件抹平浏览器之间的兼容性差异;
分成设计,解决跨平台问题;
干预事件触发,React 需要知道触发了什么事件,通过什么原生事件调用真实事件;
性能优化,避免事件直接绑定在dom上;

三、事件流程浅析

React17事件系统架构图

事件注册

初始化形成必要的事件映射关系

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// eventPriorities:原生事件及其优先级的映射 Map结构

{"click" => 0,"drag" => 1,"load" => 2}

// topLevelEventsToReactNames:原生事件和合成事件的映射 Map结构

{"cancel" => "onCancel","click" => "onClick","close" => "onClose"}

// allNativeEvents:所有有意义的原生事件名称集合 Set结构
{'cancel', 'click', 'close'}

// registrationNameDependencies:合成事件和其依赖的原生事件集合的映射
{
"onCancel": ["cancel"],
"onCancelCapture": ["cancel"],
// ...
"onChange": ["change", "click", "focusin","focusout", "input", "keydown", "keyup", "selectionchange"],
"onCancelCapture": ["change", "click", "focusin","focusout", "input", "keydown", "keyup", "selectionchange"],
"onSelect": ["focusout", "contextmenu", "dragend", "focusin", "keydown", "keyup", "mousedown", "mouseup", "selectionchange"],
"onSelectCapture": ["focusout", "contextmenu", "dragend", "focusin", "keydown", "keyup", "mousedown", "mouseup", "selectionchange"],
// ...
}

事件的优先级:
DiscreteEvent:离散事件,cancel、click、mousedown 这类单点触发不持续的事件,优先级最低
UserBlockingEvent:用户阻塞事件,drag、mousemove、wheel 这类持续触发的事件,优先级相对较高
ContinuousEvent:连续事件,load、error、waiting 这类大多与媒体相关的事件为主的事件需要及时响应,所以优先级最高

所以事件优先级:ContinuousEvent > UserBlockingEvent > DiscreteEvent

1
2
3
4
5
SimpleEventPlugin.registerEvents();
EnterLeaveEventPlugin.registerEvents();
ChangeEventPlugin.registerEvents();
SelectEventPlugin.registerEvents();
BeforeInputEventPlugin.registerEvents();

源码地址

事件绑定

获取应用中的所有原生事件,添加到监听器中,且添加带有优先级的事件处理器。

源码地址

事件触发

触发某事件,触发对应的事件处理器如:DispatchDiscreteEvent (离散事件处理器),从整个对于调用过程的进行追踪可以大致得到以下流程。

源码地址

四、小结

1、DispatchEvent 是整个事件系统的核心,包含事件的注册、存储和执行;
2、React 事件系统通过代理统一的事件触发,来调度原生事件的触发;
3、React 事件系统的合成事件优先级影响着触发更新的优先级,决定了对应更新的更新时机;

五、参考资料

React17 事件系统
React更新日志
React16.x 版本的事件系统