-
Notifications
You must be signed in to change notification settings - Fork 4
Description
事件绑定
从 createRoot 函数开始
function createRoot(container) {
...
const rootContainerElement: Document | Element | DocumentFragment =
container.nodeType === COMMENT_NODE
? (container.parentNode: any)
: container;
listenToAllSupportedEvents(rootContainerElement);
...
}listenToAllSupportedEvents 进行原生事件绑定。也会处理 nonDelegatedEvents,selectionchange 事件等
function listenToAllSupportedEvents() {
// 核心代码,listenToNativeEvent 调用 addTrappedEventListener
// eventSystemFlags |= IS_CAPTURE_PHASE;
listenToNativeEvent(domEventName, true, rootContainerElement);
}addTrappedEventListener 函数根据不同事件,构造不同优先级的处理函数(createEventListenerWrapperWithPriority),得到该函数之后进行原生 DOM 事件绑定,绑定到根 DOM 节点。比如,click 事件,处理函数就是 dispatchDiscreteEvent,默认是 dispatchEvent。并通过 bind 的方式,固定参数:domEventName、eventSystemFlags、targetContainer(即 root dom 节点实例)
至此,绑定原生事件的过程结束,后续就是等待用户交互,触发原生事件,进入诸如 dispatchEvent 函数进行处理。
事件处理
进入诸如 dispatchEvent 函数进行处理。其最总调用 dispatchEventOriginal 函数处理。
function dispatchEventOriginal() {
...
const blockedOn = findInstanceBlockingEvent(
domEventName,
eventSystemFlags,
targetContainer,
nativeEvent,
);
if (blockedOn === null) {
dispatchEventForPluginEventSystem(
domEventName,
eventSystemFlags,
nativeEvent,
return_targetInst,
targetContainer,
);
...
return;
}
...
}
function findInstanceBlockingEvent() {
// 该值会在函数中处理,有值的 2 种情况
// - targetInst NearestMountedFiber 是 Suspense
// - Hydrating 情况下,targetInst NearestMountedFiber 是 HostRoot
return_targetInst = null;
const nativeEventTarget = getEventTarget(nativeEvent);
// targetInst 是根据当前事件的 DOM 节点中存储的 internalInstanceKey 找到对应 fiber 实例
let targetInst = getClosestInstanceFromNode(nativeEventTarget);
...
return null
}blockedOn 有值的 2 种情况,有值则会调用 queueDiscreteEvent 放入到 queuedDiscreteEvents 队列(retryIfBlockedOn 会消费)
- targetInst NearestMountedFiber 是 Suspense
- 注入 Hydrating 情况下,targetInst NearestMountedFiber 是 HostRoot
- 注:targetInst 是根据当前事件的 DOM 节点中存储的 internalInstanceKey 找到对应 fiber 实例
function dispatchEventForPluginEventSystem() {
// 注意,这里的 targetInst 是👆说的 return_targetInst 这个全局变量
// 即处理 Suspense 或者 HostRoot 情况
if (targetInst !== null) {
...
}
batchedUpdates(() =>
dispatchEventsForPlugins(
domEventName,
eventSystemFlags,
nativeEvent,
ancestorInst,
targetContainer,
),
);
}dispatchEventsForPlugins 函数就是调用注册的 Plugin 的 extract 方法,提取事件。比如 SimpleEventPlugin.extractEvents 函数内会通过 accumulateSinglePhaseListeners 函数收集绑定的事件,并构造 SyntheticEvent,一起放入到 dispatchQueue 队列
function extractEvents() {
...
const listeners = accumulateSinglePhaseListeners(
targetInst,
reactName,
nativeEvent.type,
inCapturePhase,
accumulateTargetOnly,
nativeEvent,
);
if (listeners.length > 0) {
// Intentionally create event lazily.
const event = new SyntheticEventCtor(
reactName,
reactEventType,
null,
nativeEvent,
nativeEventTarget,
);
dispatchQueue.push({event, listeners});
}
...
}
function accumulateSinglePhaseListeners() {
// Accumulate all instances and listeners via the target -> root path.
}dispatchQueue 队列会在 processDispatchQueue 函数内被处理
function processDispatchQueueItemsInOrder(
event: ReactSyntheticEvent,
dispatchListeners: Array<DispatchListener>,
inCapturePhase: boolean,
): void {
let previousInstance;
if (inCapturePhase) {
// 处理捕获事件
for (let i = dispatchListeners.length - 1; i >= 0; i--) {
const {instance, currentTarget, listener} = dispatchListeners[i];
if (instance !== previousInstance && event.isPropagationStopped()) {
return;
}
executeDispatch(event, listener, currentTarget);
previousInstance = instance;
}
} else {
// 处理冒泡事件
for (let i = 0; i < dispatchListeners.length; i++) {
const {instance, currentTarget, listener} = dispatchListeners[i];
if (instance !== previousInstance && event.isPropagationStopped()) {
return;
}
executeDispatch(event, listener, currentTarget);
previousInstance = instance;
}
}
}