React 什么时候创建合成事件?



现在,这是我理解 React 中事件处理的方式:

  1. 本机事件捕获阶段发生
  2. 本机事件到达目标
  3. 本机事件重新冒泡
  4. React 在document层捕获它
  5. React 把它放在了它的EventPluginHub
  6. React 模拟了SyntheticEvent的另一个完整捕获/气泡往返
  7. React 运行你在代码中构建的处理程序

如果我对 React 内部的理解是正确的,并考虑到 HTML 规范的这一部分:

事件

对象被调度到事件目标。但在发货前 可以开始,事件对象的传播路径必须首先 确定。

React 会等到事件冒泡到document时再创建其SyntheticEvent吗?如果是这样,为什么?在事件生命的第一步中,有关其传播路径的所有信息都是已知的,因此他们可以在那里进行。

原因之一是性能。React 不是将事件侦听器附加到您在应用程序中使用的每个事件,而是将每个事件类型的单个事件侦听器附加到document。然后,当您在div上创建onClick事件时,它只是将其添加到其内部事件侦听器映射中。一旦点击div,文档节点上的 React 监听器就会找到你的事件监听器,并使用 SyntheticEvent 调用它。

这样,react 就不必一直从 DOM 重新创建和清除侦听器,它只是更改了它的内部监听器注册表。

另一个原因是,在某些情况下,与 DOM 相比,React 的事件冒泡的工作方式不同。特别是门户。

举个例子:

const MyComponent = () => (
<div onClick={() => console.log('I was clicked!')}>
MyComponent
<SomeModalThatUsesPortalComponent>A modal</SomeModalThatUsesPortalComponent>
</div>
);
const SomeModalThatUsesPortalComponent = () => {
return ReactDOM.createPortal(
<div onClick={() => console.log('Modal clicked!')}>this.props.children</div>,
document.getElementById('myModalsPortal')
);
}

这将最终得到以下 DOM:

<body>
<div>
My Component
</div>
<div id="myModalsPortal">
<div>A modal</div>
</div>
</body>

因此,在这种情况下,当使用门户时,DOM 结构与组件结构不完全匹配。这是冒泡行为分歧的地方:

  • 单击A modal不会触发MyComponent'sdiv上的本机单击事件
  • 它将在div 上触发 React 的onClick处理程序MyComponent's因为模态是MyComponent的子组件

在 React 源代码中没有提到等待事件冒泡以调度SyntheticEvents 的原因(我可以找到(。

等待冒泡的事件完成冒泡,然后再将它们困在 React 事件系统中,这显然只是代码组织的问题

阅读 React 的 ReactBrowserEventEmitterlistenTo函数,此处进行了简化以使其更容易理解:

for (let i = 0; i < dependencies.length; i++) {
const dependency = dependencies[i];
switch (dependency) {
case: // All event types that do NOT bubble
trapCapturedEvent(dependency, mountAt);
default:
// By default, listen on the top level to all non-media events.
// Media events don't bubble so adding the listener wouldn't do anything.
const isMediaEvent = mediaEventTypes.indexOf(dependency) !== -1;
if (!isMediaEvent) {
trapBubbledEvent(dependency, mountAt);
}
break;
}
isListening[dependency] = true;
}

将冒泡的事件与不冒泡的事件(并保持代码可读性(区分开来的最简单方法似乎是捕获在冒泡阶段冒泡的事件,以及在捕获阶段不冒泡的事件。

因此,总而言之,事件的实际运作方式(与我在问题中的初始命题进行类比(如下:

对于冒泡的事件:

  1. 本机事件捕获阶段发生
  2. 本机事件到达目标
  3. 本机事件重新冒泡
  4. React 在文档层用(抽象版本(addEventListener(type, handler, false) // False stands for "catch it in the bubbling phase"捕获它
  5. React 把它放在了它的ReactBrowserEventEmitter
  6. React 模拟了合成事件的另一个完整捕获/气泡往返
  7. React 运行你在代码中构建的处理程序

对于不冒泡的事件:

  1. 本机事件捕获阶段发生
  2. React 在你用(抽象版本(在代码中设置侦听器的层捕获它addEventListener(type, handler, true) // True stands for "catch it in the capture phase"
  3. React 把它放在它的ReactBrowserEventEmitter
  4. React 模拟了合成事件的另一个完整捕获/气泡往返
  5. React 运行你在代码中构建的处理程序

React 通过在文档上添加所有 react 事件处理程序来进行顶级委派。因此,在捕获和气泡阶段会发生以下情况。

捕获阶段:

  1. 捕获文档上的事件侦听器(本机 DOMEvents(
  2. 捕获反应元素上的事件侦听器 (合成事件(
  3. 捕获文档子级上的事件侦听器(本机 DOMEvents(

冒泡阶段:

  1. 文档子级上的气泡事件侦听器(本机 DOMEvents(
  2. 反应元素上的气泡事件侦听器 (合成事件(
  3. 文档上的气泡事件侦听器(本机 DOMEvents(

最新更新