Skip to content

05.派发事件的主要逻辑

packages/react-dom-bindings/src/events/ReactDOMEventListener.js

js
import getEventTarget from './getEventTarget';
import { getClosestInstanceFromNode } from '../client/ReactDOMComponentTree';
import { dispatchEventForPluginEventSystem } from './DOMPluginEventSystem';

function dispatchDiscreteEvent(domEventName, eventSystemFlags, container, nativeEvent) {
  dispatchEvent(domEventName, eventSystemFlags, container, nativeEvent);
}

/**
 * 调度一个事件。
 *
 * @param {string} domEventName - DOM事件名称。
 * @param {number} eventSystemFlags - 事件系统标志,用于表示事件在哪个阶段(冒泡/捕获)。
 * @param {HTMLElement} targetContainer - 目标容器,通常是一个HTML元素。
 * @param {Event} nativeEvent - 原生的浏览器事件对象。
 */
export function dispatchEvent(domEventName, eventSystemFlags, targetContainer, nativeEvent) {
  const nativeEventTarget = getEventTarget(nativeEvent);
  const targetInst = getClosestInstanceFromNode(nativeEventTarget);
  dispatchEventForPluginEventSystem(
    domEventName,
    eventSystemFlags,
    nativeEvent,
    targetInst,
    targetContainer
  );
}

packages/react-dom-bindings/src/events/getEventTarget.js

js
/**
 * 获取原生事件的目标元素。如果原生事件没有目标元素,
 * 则尝试获取事件的 `srcElement`,如果仍然没有,则返回全局 `window` 对象。
 *
 * @param {Event} nativeEvent - 原生的 DOM 事件对象
 * @returns {EventTarget|Window} - 事件的目标元素或 `window` 对象
 */
function getEventTarget(nativeEvent) {
  const target = nativeEvent.target || nativeEvent.srcElement || window;
  return target;
}

export default getEventTarget;

packages/react-dom-bindings/src/client/ReactDOMComponentTree.js

js
const randomKey = Math.random().toString(36).slice(2);
const internalInstanceKey = '__reactFiber$' + randomKey;
const internalPropsKey = "__reactProps$" + randomKey;


export function precacheFiberNode(hostInst, node) {
    node[internalInstanceKey] = hostInst;
}

export function updateFiberProps(node, props) {
    node[internalPropsKey] = props;
}

export function getClosestInstanceFromNode(targetNode) {
    const targetInst = targetNode[internalInstanceKey]
    return targetInst;
}

packages/react-dom-bindings/src/client/ReactDOMHostConfig.js

js
import { precacheFiberNode, updateFiberProps } from './ReactDOMComponentTree';
export function createInstance(type, props, internalInstanceHandle) {
  const domElement = document.createElement(type);
   precacheFiberNode(internalInstanceHandle, domElement);
   updateFiberProps(domElement, props);
  return domElement;
}

packages/react-dom-bindings/src/events/DOMPluginEventSystem.js

js
import getEventTarget from './getEventTarget';

/**
 * 在插件事件系统中分发事件
 * @param {string} domEventName DOM事件名称
 * @param {number} eventSystemFlags 事件系统标记
 * @param {Event} nativeEvent 原生事件
 * @param {Fiber} targetInst Fiber目标实例
 * @param {Element} targetContainer 目标容器元素
 */
export function dispatchEventForPluginEventSystem(
  domEventName, eventSystemFlags, nativeEvent, targetInst, targetContainer
) {
  dispatchEventForPlugins(domEventName, eventSystemFlags, nativeEvent, targetInst, targetContainer)
}

/**
 * 为插件分发事件
 * @param {string} domEventName DOM事件名称
 * @param {number} eventSystemFlags 事件系统标记
 * @param {Event} nativeEvent 原生事件
 * @param {Fiber} targetInst Fiber目标实例
 * @param {Element} targetContainer 目标容器元素
 */
function dispatchEventForPlugins(domEventName, eventSystemFlags, nativeEvent, targetInst, targetContainer) {
  // 获取原生事件的目标
  const nativeEventTarget = getEventTarget(nativeEvent);
  const dispatchQueue = [];
  // 提取事件
  extractEvents(
    dispatchQueue,
    domEventName,
    targetInst,
    nativeEvent,
    nativeEventTarget,
    eventSystemFlags,
    targetContainer
  );
  // 处理分发队列
  processDispatchQueue(dispatchQueue, eventSystemFlags);
}

/**
 * 处理分发队列
 * @param {Array} dispatchQueue 分发队列
 * @param {number} eventSystemFlags 事件系统标记
 */
function processDispatchQueue(dispatchQueue, eventSystemFlags) {
}

/**
 * 提取事件
 * @param {Array} dispatchQueue 分发队列
 * @param {string} domEventName DOM事件名称
 * @param {Fiber} targetInst Fiber目标实例
 * @param {Event} nativeEvent 原生事件
 * @param {EventTarget} nativeEventTarget 原生事件目标
 * @param {number} eventSystemFlags 事件系统标记
 * @param {Element} targetContainer 目标容器元素
 */
function extractEvents(dispatchQueue,
  domEventName,
  targetInst,
  nativeEvent,
  nativeEventTarget,
  eventSystemFlags,
  targetContainer) {
  SimpleEventPlugin.extractEvents(
    dispatchQueue,
    domEventName,
    targetInst,
    nativeEvent,
    nativeEventTarget,
    eventSystemFlags,
    targetContainer
  );
}

/**
 * 累积单阶段监听器
 * @param {Fiber} targetFiber 目标Fiber实例
 * @param {string} reactName React事件名称
 * @param {string} nativeEventType 原生事件类型
 * @param {boolean} isCapturePhase 是否在捕获阶段
 */
export function accumulateSinglePhaseListeners(
  targetFiber, reactName, nativeEventType, isCapturePhase
) {

}

packages/react-dom-bindings/src/events/plugins/SimpleEventPlugin.js

js
// 导入需要的模块和函数
import { registerSimpleEvents, topLevelEventsToReactNames } from '../DOMEventProperties';
import { IS_CAPTURE_PHASE } from '../EventSystemFlags';
import { accumulateSinglePhaseListeners } from '../DOMPluginEventSystem';
import { SyntheticMouseEvent } from '../SyntheticEvent';

/**
 * 提取特定事件并将其加入调度队列
 *
 * @param {Array} dispatchQueue 要处理的事件队列
 * @param {string} domEventName DOM 事件的名称,例如 'click'
 * @param {Object} targetInst 目标实例,接收事件的 React 组件
 * @param {Event} nativeEvent 原生的浏览器事件对象
 * @param {EventTarget} nativeEventTarget 原生的浏览器事件目标
 * @param {number} eventSystemFlags 事件系统标志,表示特定的事件状态
 * @param {Element} targetContainer 事件发生的 DOM 容器
 */
function extractEvents(
  dispatchQueue,
  domEventName,
  targetInst,
  nativeEvent,
  nativeEventTarget,
  eventSystemFlags,
  targetContainer) {
  // 根据给定的 DOM 事件名,获取对应的 React 事件名
  const reactName = topLevelEventsToReactNames.get(domEventName);
  let SyntheticEventCtor;
  // 根据 DOM 事件名来确定要使用的合成事件构造函数
  switch (domEventName) {
    case 'click':
      SyntheticEventCtor = SyntheticMouseEvent;  // 对于 'click' 事件,使用 SyntheticMouseEvent
      break;
    default:
      break;
  }
  // 通过与运算确定事件是否处于捕获阶段
  const isCapturePhase = (eventSystemFlags & IS_CAPTURE_PHASE) !== 0;
  // 使用 accumulateSinglePhaseListeners 函数获取当前阶段的所有事件监听器
  const listeners = accumulateSinglePhaseListeners(
    targetInst,
    reactName,
    nativeEvent.type,
    isCapturePhase
  );
  // 如果存在至少一个监听器
  if (listeners.length > 0) {
    // 则创建一个新的合成事件
    const event = new SyntheticEventCtor(
      reactName, domEventName, null, nativeEvent, nativeEventTarget);
    // 并将其与相应的监听器一起加入调度队列
    dispatchQueue.push({
      event,
      listeners
    });
  }
}

// 导出函数,重命名 registerSimpleEvents 为 registerEvents
export { registerSimpleEvents as registerEvents, extractEvents }

packages/react-dom-bindings/src/events/SyntheticEvent.js

js
export const SyntheticMouseEvent = ()=>{}

基于 VitePress 构建