React

关注公众号 jb51net

关闭
首页 > 网络编程 > JavaScript > javascript类库 > React > React  listenToAllSupportedEvents

React 事件系统listenToAllSupportedEvents的实现

作者:YFF菲菲兔

本文主要介绍了React 事件系统listenToAllSupportedEvents的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

React DOM 事件系统的初始化入口,在 createRoot 时调用,接收 React 根容器 DOM 作为入参,一次性给根容器挂载全部 React 支持的原生事件监听器,实现 React 事件委托(合成事件底层核心)

完整初始化流程

应用启动:
  createRoot(document.getElementById('root'))
    → listenToAllSupportedEvents(containerDiv)
      │
      ├─ containerDiv['_reactListening' + rand] = true
      │
      ├─ allNativeEvents = { click, keydown, mouseup, scroll, wheel, ... ~85 个 }
      │
      ├─ click (非 nonDelegatedEvents):
      │   ├─ listenToNativeEvent('click', false, containerDiv)
      │   │   → addTrappedEventListener(containerDiv, 'click', 0, false)
      │   │     → createEventListenerWrapperWithPriority(container, 'click', 0)
      │   │       → getEventPriority('click') → DiscreteEventPriority
      │   │       → wrapper = dispatchDiscreteEvent.bind(null, 'click', 0, container)
      │   │     → containerDiv.addEventListener('click', wrapper, false)   ← bubble
      │   └─ listenToNativeEvent('click', true, containerDiv)
      │       → addTrappedEventListener(containerDiv, 'click', IS_CAPTURE, true)
      │         → containerDiv.addEventListener('click', wrapper, true)    ← capture
      │
      ├─ scroll (nonDelegatedEvents):
      │   └─ listenToNativeEvent('scroll', true, containerDiv)  ← 只有 capture!
      │       → containerDiv.addEventListener('scroll', wrapper, true)
      │
      ├─ touchstart (nonDelegatedEvents 无, 但 passive):
      │   ├─ listenToNativeEvent('touchstart', false, containerDiv)
      │   │   → isPassiveListener = true
      │   │   → containerDiv.addEventListener('touchstart', wrapper, { passive: true })
      │   └─ listenToNativeEvent('touchstart', true, containerDiv)
      │       → container.addEventListener('touchstart', wrapper, { capture: true, passive: true })
      │
      └─ selectionchange:
          ├─ ownerDocument = document
          └─ document['_reactListening' + rand] = true
          └─ listenToNativeEvent('selectionchange', false, document)
            → document.addEventListener('selectionchange', wrapper, false)

绑定后 DOM 上的结果

<div id="root" (containerDiv)>
  ├─ click (bubble): listener
  ├─ click (capture): listener
  ├─ keydown (bubble): listener
  ├─ keydown (capture): listener
  ├─ scroll (capture): listener  ← 无 bubble
  ├─ wheel (bubble, passive): listener
  ├─ wheel (capture, passive): listener
  ├─ touchstart (bubble, passive): listener
  ├─ touchstart (capture, passive): listener
  └─ ... (~85 个事件,每个事件 1-2 个 listener)
      ...
document:
  └─ selectionchange (bubble): listener

源码解析

重复绑定拦截判断

const listeningMarker = '_reactListening' + Math.random().toString(36).slice(2);

if (!(rootContainerElement: any)[listeningMarker]) {
  (rootContainerElement: any)[listeningMarker] = true;
}

作用

判断根容器 DOM 上是否已经挂载过 listeningMarker 标记

设计意义

整个事件监听初始化是一次性的:所有事件的 listener 在 rootContainer 创建时注册一次,之后 fiber 的 mount/update/unmount 通过 __reactFiber$<hash> 和 __reactProps$<hash> 从 DOM 节点获取当前事件处理函数,不需要重新注册

遍历所有原生支持事件

allNativeEvents.forEach(domEventName => {
  if (domEventName !== 'selectionchange') {
    if (!nonDelegatedEvents.has(domEventName)) {
      // 委托事件 → rootContainer 上添加 bubble 监听
      listenToNativeEvent(domEventName, false, rootContainerElement);
    }
    // 全部事件 → rootContainer 上添加 capture 监听
    listenToNativeEvent(domEventName, true, rootContainerElement);
  }
});

作用

事件的监听策略

条件BubbleCapture示例
委托事件click: bubble + capture
非委托事件scroll: capture only
selectionchange❌(跳过)❌(跳过)单独处理

设计意义

处理 document 与 selectionchange

const ownerDocument =
  rootContainerElement.nodeType === DOCUMENT_NODE
    ? rootContainerElement
    : rootContainerElement.ownerDocument;

if (ownerDocument !== null) {
  if (!ownerDocument[listeningMarker]) {
    ownerDocument[listeningMarker] = true;
    listenToNativeEvent('selectionchange', false, ownerDocument);
  }
}

作用

设计意义

selectionchange 是 React 事件系统中唯一一个不在 rootContainer 上监听的事件。它作为特例在 listenToAllSupportedEvents 中单独处理,使用独立的 listeningMarker 防重,同一 document 的多个 root 共享同一个 document 监听

辅助函数

listenToNativeEvent

接收事件名、捕获 / 冒泡标识、绑定目标 DOM,组装事件标记位,调用底层真正注册监听的函数 addTrappedEventListener

export function listenToNativeEvent(
  domEventName,
  isCapturePhaseListener,
  target,
) {

  let eventSystemFlags = 0;
  // 标记当前监听处于捕获阶段
  if (isCapturePhaseListener) {
    eventSystemFlags |= IS_CAPTURE_PHASE;
  }
  addTrappedEventListener(
    target,
    domEventName,
    eventSystemFlags,
    isCapturePhaseListener,
  );
}

作用

设计思想

到此这篇关于React 事件系统listenToAllSupportedEvents的实现的文章就介绍到这了,更多相关React listenToAllSupportedEvents内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文