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 = ()=>{}