责任编辑译者: 河水
序言
React 为他们提供更多了两套交互式的该事件控制系统,这套交互式该事件控制系统是怎样组织工作的,本栏对源标识符做了一次剖析,重新整理了下面的文件格式供大家参照。
在 React该事件如是说中如是说了制备该事件第一类以及为什么提供更多合成该事件第一类,主要就原因原因在于 React 想同时实现三个全应用领域程序的架构, 为了同时实现此种目标就须要提供更多全应用领域程序连续性的该事件控制系统,以认是相同应用领域程序的差别。
制备该事件第一类很有趣,一开始听英文名字会觉得很怪异,看到英文名更怪异 SyntheticEvent, 事实上制备该事件的原意就是采用原生植物该事件制备三个 React 该事件, 比如采用原生植物click该事件制备了onClick该事件,采用原生植物mouseout该事件制备了onMouseLeave该事件,原生植物该事件和制备该事件类别绝大部分都是一一相关联,只有牵涉到兼容问题时他们才须要采用不相关联的该事件制备。制备该事件并并非 React 的首开,在 iOS 上碰到的 300ms 问题而引入的 fastclick 就采用了 touch 该事件制备了 click 该事件,也算一种制备该事件的应用领域。
了解了 React 该事件是制备该事件之后他们审视该事件的视角就会有所相同, 比如他们时常在标识符中写的这种标识符
他们已经晓得那个onClick只是三个制备该事件而不是原生植物该事件, 那这几天到底发生了什么? 原生植物该事件和制备该事件是怎样相关联出来的?
下面的标识符看出来很简约,事实上 React 该事件控制系统组织工作监督机制较之下面要繁杂的多,坐板凳脏活全被在下层处置了, 实在架构劳动模范。其组织工作基本原理大体分为三个期
该事件存取该事件促发下面就一出来看下这三个期到底是怎样组织工作的, 这里主要就从源标识符层分析,并以 16.13 源标识符中内容为计算方法。
1. React 是怎样存取该事件的 ?
React 既然提供更多了制备该事件,就须要晓得制备该事件与原生植物该事件是怎样相关联出来的,那个相关联关系存放在 React 该事件应用领域程序中EventPlugin, 该事件应用领域程序能认为是 React 将相同的制备该事件处置表达式PCB成了三个组件,每一组件只处置自己相关联的制备该事件,这样相同类别的该事件类型就能在标识符拉苏特兰耦,比如特别针对onChange该事件有三个原则上的LegacyChangeEventPlugin应用领域程序来处置,特别针对onMouseEnter, onMouseLeave 采用 LegacyEnterLeaveEventPlugin 应用领域程序来处置。
为了晓得制备该事件与原生植物该事件的相关联关系,React 在一开始就将该事件应用领域程序全部加载进来, 这部分逻辑在ReactDOMClientInjection 标识符如下
注册完上述应用领域程序后, EventPluginRegistry(老版本标识符里那个组件唤作EventPluginHub)那个组件里就初始化好了一些全局第一类,有几个第一类比较重要,能原则上说一下。
第三个第一类是 registrationNameModule, 它包含了 React 该事件到它相关联的 plugin 的映射, 大致长下面这样,它包含了 React 所支持的所有该事件类别,那个第一类最大的作用是判断三个组件的 prop 是否是该事件类别,这在处置原生植物组件的 props 时候将会用到,如果三个 prop 在那个第一类中才会被当做该事件处置。
第二个第一类是 registrationNameDependencies, 那个第一类长下面几个样子
那个第一类即是一开始他们说到的制备该事件到原生植物该事件的映射,对于onClick 和 onClickCapture该事件, 只依赖原生植物click该事件。但是对于 onMouseLeave它却是依赖了三个mouseout, mouseover, 这说明那个该事件是 React 采用 mouseout 和 mouseover 模拟制备的。正原因在于此种行为,使得 React 能够制备一些哪怕应用领域程序不支持的该事件供他们标识符里采用。
第三个第一类是 plugins, 那个第一类就是下面注册的所有应用领域程序列表。
看完下面这些信息后他们再反过头来看下三个普通的EventPlugin长什么样子。三个 plugin 就是三个第一类, 那个第一类包含了下面三个属性
了解下面这这些信息对他们分析 React 该事件组织工作基本原理将会很有帮助,下面开始进入该事件存取期。
React 执行 diff 操作,标记出哪些 DOM 类别 的节点须要添加或者更新。当检测到须要创建三个节点或者更新三个节点时, 采用 registrationNameModule 查看三个 prop 是并非三个该事件类别,如果是则执行下一步。通过 registrationNameDependencies 检查那个 React 该事件依赖了哪些原生植物该事件类别。检查这些三个或多个原生植物该事件类别有没有注册过,如果有则忽略。如果那个原生植物该事件类别没有注册过,则注册那个原生植物该事件到 document 上,回调为React提供更多的dispatchEvent表达式。下面的期说明:
他们将所有该事件类别都注册到 document 上。所有原生植物该事件的 listener 都是dispatchEvent表达式。同三个类别的该事件 React 只会存取一次原生植物该事件,比如无论他们写了多少个onClick, 最终反应在 DOM 该事件上只会有三个listener。React 并没有将他们业务逻辑里的listener绑在原生植物该事件上,也没有去维护三个类似eventlistenermap的东西存放他们的listener。由 3,4 条规则能得出,他们业务逻辑的listener和实际 DOM 该事件压根就没关系,React 只是会确保那个原生植物该事件能够被它自己捕捉到,后续由 React 来派发他们的该事件回调,当他们页面发生较大的切换时候,React 能什么都不做,从而免去了去操作removeEventListener或者同步eventlistenermap的操作,所以其执行效率将会大大提高,相当于全局给我们做了一次该事件委托,即便是渲染大列表,也不用开发者关心该事件存取问题。
2. React 是怎样促发该事件的?
他们晓得由于所有类别类型的该事件都是存取为React的 dispatchEvent 表达式,所以就能在全局处置一些通用行为,下面就是整个行为过程。
bookKeeping为该事件执行时组件的层级关系存储,也就是如果在该事件执行过程中发生组件结构变更,并不会影响该事件的促发流程。
整个促发该事件流程如下:
任意三个该事件促发,执行 dispatchEvent 表达式。dispatchEvent 执行 batchedEventUpdates(handleTopLevel),batchedEventUpdates 会打开批量渲染开关并调用 handleTopLevel。handleTopLevel会依次执行 plugins 里所有的该事件应用领域程序。如果三个应用领域程序检测到自己须要处置的该事件类别时,则处置该该事件。对于绝大部分该事件而言其处置逻辑如下,也即 LegacySimpleEventPlugin 应用领域程序做的组织工作
通过原生植物该事件类别决定采用哪个制备该事件类别(原生植物 event 的PCB第一类,比如 SyntheticMouseEvent) 。如果第一类池里有那个类别的实例,则取出那个实例,覆盖其属性,作为本次派发的该事件第一类(该事件第一类复用),若没有则新建三个实例。从点击的原生植物该事件中找到相关联 DOM 节点,从 DOM 节点中找到三个最近的React组件实例, 从而找到了一条由那个实例父节点不断向上组成的链, 那个链就是他们要促发制备该事件的链,(只包含原生植物类别组件, div, a 此种原生植物组件)。反向促发这条链,父-> 子,模拟捕获期,促发所有 props 中含有 onClickCapture 的实例。正向促发这条链,子-> 父,模拟冒泡期,促发所有 props 中含有 onClick 的实例。这几个期说明了下面的现象:
React 的制备该事件只能在该事件周期内采用,因为那个第一类很可能被其他期复用, 如果想持久化须要手动调用event.persist() 告诉 React 那个第一类须要持久化。( React17 中被废弃)React 的冒泡和捕获并并非真正 DOM 级别的冒泡和捕获React 会在三个原生植物该事件里促发所有相关节点的 onClick 该事件, 在执行这些onClick之前 React 会打开批量渲染开关,那个开关会将所有的setState变成异步表达式。该事件只特别针对原生植物组件生效,自定义组件不会促发 onClick。3. 从React 的该事件控制系统中他们学到了什么
React16 将原生植物该事件都存取在 document 上.这点很好理解,React的该事件事实上都是在document上促发的。
他们收到的 event 第一类为 React 制备该事件, event 第一类在该事件之外不能采用所以下面就是错误用法
此时 1, 2 在该事件内所以是异步的,二者只会促发一次 render 操作,3, 4 是同步的,3,4 分别都会促发一次 render。
React onClick/onClickCapture, 事实上都发生在原生植物该事件的冒泡期。这里他们虽然采用了onClickCapture, 但事实上对原生植物该事件而言依然是冒泡,所以 React 16 中事实上就不支持存取捕获该事件。
由于所有该事件都注册到顶层该事件上,所以多实个 ReactDOM.render 会存在冲突。如果他们渲染三个子树采用另三个版本的 React 实例创建, 那么即使在子树中调用了 e.stopPropagatio 该事件依然会传播。所以多版本的 React 在该事件上存在冲突。
最后他们就能轻松理解 React 该事件控制系统的架构图了
4. React 17 中该事件控制系统有哪些新特性
React 17 目前已经发布了, 官方称之为没有新特性的更新, 对于采用者而言没有提供更多类似 Hooks 这样爆炸的特性,也没有 Fiber 这样的重大重构,而是积攒了大量 Bugfix,修复了之前存在的诸多缺陷。其中变化最大的就数对该事件控制系统的改造了。
下面是本栏列举的一些该事件相关的特性更新
调整将顶层该事件绑在container上,ReactDOM.render(app, container);
将顶层该事件存取在 container 上而并非 document 上能够解决他们碰到的多版本共存问题,对微前端方案是个重大利好。
对齐原生植物应用领域程序该事件
React 17 中终于支持了原生植物捕获该事件的支持, 对齐了应用领域程序原生植物标准。
同时onScroll 该事件不再进行该事件冒泡。
onFocus 和 onBlur 采用原生植物 focusin, focusout 制备。
Aligning with Browsers
We’ve made a couple of smaller changes related to the event system:
The onScroll event no longer bubbles to prevent common confusion.
React onFocus and onBlur events have switched to using the native focusin and focusout events under the hood, which more closely match React’s existing behavior and sometimes provide extra information.
Capture phase events (e.g. onClickCapture) now use real browser capture phase listeners.取消该事件复用
官方的解释是该事件第一类的复用在现代应用领域程序上性能已经提高的不明显了,反而还很容易让人用错,所以干脆就放弃那个优化。
参照
https://reactjs.org/docs/events.htmlhttps://reactjs.org/docs/handling-events.htmlhttps://github.com/facebook/react责任编辑发布自 网易云音乐大前端团队,文章未经授权禁止任何形式的转载。他们常年招收前端、iOS、Android,如果你准备换组织工作,又恰好喜欢云音乐,那就加入他们 grp.music-fe(at)corp.netease.com!