React DOM event listener
一、作用
二、设置为启用
// This is exported in FB builds for use by legacy FB layer infra.
// We'd like to remove this but it's not clear if this is safe.
// 这是在 FB 构建中导出的,用于旧版 FB 层基础设施。
// 我们希望删除它,但不清楚这样是否安全。
export function setEnabled(enabled: ?boolean): void {
_enabled = !!enabled;
}
三、是否为启用
export function isEnabled(): boolean {
return _enabled;
}
四、创建事件监听器包装器
export function createEventListenerWrapper(
targetContainer: EventTarget,
domEventName: DOMEventName,
eventSystemFlags: EventSystemFlags,
): Function {
return dispatchEvent.bind(
null,
domEventName,
eventSystemFlags,
targetContainer,
);
}
五、创建带优先级的事件监听器包装器
备注
DiscreteEventPriority由 ReactEventPriorities#DiscreteEventPriority 提供ContinuousEventPriority由 ReactEventPriorities#ContinuousEventPriority 提供DefaultEventPriority由 ReactEventPriorities#DefaultEventPriority 提供
export function createEventListenerWrapperWithPriority(
targetContainer: EventTarget,
domEventName: DOMEventName,
eventSystemFlags: EventSystemFlags,
): Function {
const eventPriority = getEventPriority(domEventName);
let listenerWrapper;
switch (eventPriority) {
case DiscreteEventPriority:
listenerWrapper = dispatchDiscreteEvent;
break;
case ContinuousEventPriority:
listenerWrapper = dispatchContinuousEvent;
break;
case DefaultEventPriority:
default:
listenerWrapper = dispatchEvent;
break;
}
return listenerWrapper.bind(
null,
domEventName,
eventSystemFlags,
targetContainer,
);
}
六、分发事件
备注
clearIfContinuousEvent()由 ReactDOMEventReplaying#clearIfContinuousEvent 实现queueIfContinuousEvent()由 ReactDOMEventReplaying#queueIfContinuousEvent 实现isDiscreteEventThatRequiresHydration()由 ReactDOMEventReplaying#isDiscreteEventThatRequiresHydration 实现attemptSynchronousHydration()由 ReactFiberReconciler#attemptSynchronousHydration 实现dispatchEventForPluginEventSystem()由 DOMPluginEventSystem 提供getInstanceFromNode()由 ReactDOMComponentTree 提供dispatchEventForPluginEventSystem()由 DOMPluginEventSystem 提供
export function dispatchEvent(
domEventName: DOMEventName,
eventSystemFlags: EventSystemFlags,
targetContainer: EventTarget,
nativeEvent: AnyNativeEvent,
): void {
if (!_enabled) {
return;
}
let blockedOn = findInstanceBlockingEvent(nativeEvent);
if (blockedOn === null) {
dispatchEventForPluginEventSystem(
domEventName,
eventSystemFlags,
nativeEvent,
return_targetInst,
targetContainer,
);
clearIfContinuousEvent(domEventName, nativeEvent);
return;
}
if (
queueIfContinuousEvent(
blockedOn,
domEventName,
eventSystemFlags,
targetContainer,
nativeEvent,
)
) {
nativeEvent.stopPropagation();
return;
}
// We need to clear only if we didn't queue because
// queueing is accumulative.
// 我们只需要在没有排队的情况下清理,
// 因为排队是累积的。
clearIfContinuousEvent(domEventName, nativeEvent);
if (
eventSystemFlags & IS_CAPTURE_PHASE &&
isDiscreteEventThatRequiresHydration(domEventName)
) {
while (blockedOn !== null) {
const fiber = getInstanceFromNode(blockedOn);
if (fiber !== null) {
attemptSynchronousHydration(fiber);
}
const nextBlockedOn = findInstanceBlockingEvent(nativeEvent);
if (nextBlockedOn === null) {
dispatchEventForPluginEventSystem(
domEventName,
eventSystemFlags,
nativeEvent,
return_targetInst,
targetContainer,
);
}
if (nextBlockedOn === blockedOn) {
break;
}
blockedOn = nextBlockedOn;
}
if (blockedOn !== null) {
nativeEvent.stopPropagation();
}
return;
}
// This is not replayable so we'll invoke it but without a target,
// in case the event system needs to trace it.
// 这不可重放,所以我们会调用它,但没有目标,
// 以防事件系统需要跟踪它。
dispatchEventForPluginEventSystem(
domEventName,
eventSystemFlags,
nativeEvent,
null,
targetContainer,
);
}
七、查找实例阻塞事件
备注
getEventTarget()由 getEventTarget 实现
export function findInstanceBlockingEvent(
nativeEvent: AnyNativeEvent,
): null | Container | SuspenseInstance | ActivityInstance {
const nativeEventTarget = getEventTarget(nativeEvent);
return findInstanceBlockingTarget(nativeEventTarget);
}
八、导出的变量
1. 返回目标实例
备注
该代码在源码的 244 行
export let return_targetInst: null | Fiber = null;
九、查找实例阻塞目标
备注
getNearestMountedFiber()由 ReactFiberTreeReflection#getNearestMountedFiber 实现getSuspenseInstanceFromFiber()由 ReactFiberTreeReflection#getSuspenseInstanceFromFiber 实现getActivityInstanceFromFiber()由 ReactFiberTreeReflection#getActivityInstanceFromFiber 实现getContainerFromFiber()由 ReactFiberTreeReflection#getContainerFromFiber 实现getClosestInstanceFromNode()由 ReactDOMComponentTree 提供
// Returns a SuspenseInstance, ActivityInstance or Container if it's blocked.
// The return_targetInst field above is conceptually part of the return value.
// 如果被阻塞,返回一个 SuspenseInstance、ActivityInstance 或 Container。
// 上面的 return_targetInst 字段在概念上是返回值的一部分。
export function findInstanceBlockingTarget(
targetNode: Node,
): null | Container | SuspenseInstance | ActivityInstance {
// TODO: Warn if _enabled is false.
// TODO: 如果 _enabled 为 false,则发出警告。
return_targetInst = null;
let targetInst = getClosestInstanceFromNode(targetNode);
if (targetInst !== null) {
const nearestMounted = getNearestMountedFiber(targetInst);
if (nearestMounted === null) {
// This tree has been unmounted already. Dispatch without a target.
// 这棵树已经被卸载。无法分发,因为没有目标。
targetInst = null;
} else {
const tag = nearestMounted.tag;
if (tag === SuspenseComponent) {
const instance = getSuspenseInstanceFromFiber(nearestMounted);
if (instance !== null) {
// Queue the event to be replayed later. Abort dispatching since we
// don't want this event dispatched twice through the event system.
// TODO: If this is the first discrete event in the queue. Schedule an increased
// priority for this boundary.
// 将事件加入队列,以便稍后重放。中止分发,因为我们不希望此事件通过事件系统
// 被分发两次。
// TODO: 如果这是队列中的第一个离散事件。为此边界安排更高的优先级。
return instance;
}
// This shouldn't happen, something went wrong but to avoid blocking
// the whole system, dispatch the event without a target.
// TODO: Warn.
// 这不应该发生,出现了一些错误,但为了避免阻塞整个系统,事件将在没有目标的情况
// 下分发。
// TODO: 发出警告。
targetInst = null;
} else if (tag === ActivityComponent) {
const instance = getActivityInstanceFromFiber(nearestMounted);
if (instance !== null) {
// Queue the event to be replayed later. Abort dispatching since we
// don't want this event dispatched twice through the event system.
// TODO: If this is the first discrete event in the queue. Schedule an increased
// priority for this boundary.
// 将事件加入队列,以便稍后重放。中止分发,因为我们不希望此事件通过事件系统
// 被分发两次。
// TODO: 如果这是队列中的第一个离散事件。为此边界安排更高的优先级。
return instance;
}
// This shouldn't happen, something went wrong but to avoid blocking
// the whole system, dispatch the event without a target.
// TODO: Warn.
// 这不应该发生,出现了一些错误,但为了避免阻塞
// 整个系统,事件将在没有目标的情况下分发。
// TODO: 发出警告。
targetInst = null;
} else if (tag === HostRoot) {
const root: FiberRoot = nearestMounted.stateNode;
if (isRootDehydrated(root)) {
// If this happens during a replay something went wrong and it might block
// the whole system.
// 如果在回放过程中发生这种情况,说明出了问题,可能会阻塞整个系统。
return getContainerFromFiber(nearestMounted);
}
targetInst = null;
} else if (nearestMounted !== targetInst) {
// If we get an event (ex: img onload) before committing that
// component's mount, ignore it for now (that is, treat it as if it was an
// event on a non-React tree). We might also consider queueing events and
// dispatching them after the mount.
// 如果在提交该组件的挂载之前收到一个事件(例如:img 的 onload),暂时忽略它
// (也就是说,将其视为非 React 树上的事件)。我们也可以考虑将事件排队,并在
// 挂载后分发它们。
targetInst = null;
}
}
}
return_targetInst = targetInst;
// We're not blocked on anything.
// 我们没有被任何事情阻碍。
return null;
}
十、获取事件优先级
备注
getCurrentSchedulerPriorityLevel()由 Scheduler#getCurrentSchedulerPriorityLevel 实现ImmediateSchedulerPriority由 Scheduler#ImmediateSchedulerPriority 提供DiscreteEventPriority由 ReactEventPriorities#DiscreteEventPriority 提供UserBlockingSchedulerPriority由 Scheduler#UserBlockingSchedulerPriority 提供ContinuousEventPriority由 ReactEventPriorities#ContinuousEventPriority 提供NormalSchedulerPriority由 Scheduler#NormalSchedulerPriority 提供LowSchedulerPriority由 Scheduler#LowSchedulerPriority 提供DefaultEventPriority由 ReactEventPriorities#DefaultEventPriority 提供IdleSchedulerPriority由 Scheduler#IdleSchedulerPriority 提供IdleEventPriority由 ReactEventPriorities#IdleEventPriority 提供
export function getEventPriority(domEventName: DOMEventName): EventPriority {
switch (domEventName) {
// Used by SimpleEventPlugin:
// 由 SimpleEventPlugin 使用:
case 'beforetoggle':
case 'cancel':
case 'click':
case 'close':
case 'contextmenu':
case 'copy':
case 'cut':
case 'auxclick':
case 'dblclick':
case 'dragend':
case 'dragstart':
case 'drop':
case 'focusin':
case 'focusout':
case 'input':
case 'invalid':
case 'keydown':
case 'keypress':
case 'keyup':
case 'mousedown':
case 'mouseup':
case 'paste':
case 'pause':
case 'play':
case 'pointercancel':
case 'pointerdown':
case 'pointerup':
case 'ratechange':
case 'reset':
case 'seeked':
case 'submit':
case 'toggle':
case 'touchcancel':
case 'touchend':
case 'touchstart':
case 'volumechange':
// Used by polyfills: (fall through)
// 被 polyfills 使用:(穿透)
case 'change':
case 'selectionchange':
case 'textInput':
case 'compositionstart':
case 'compositionend':
case 'compositionupdate':
// Only enableCreateEventHandleAPI: (fall through)
// 仅启用 CreateEventHandleAPI: (继续执行)
case 'beforeblur':
case 'afterblur':
// Not used by React but could be by user code: (fall through)
// React 不使用,但可能被用户代码使用:(继续执行)
case 'beforeinput':
case 'blur':
case 'fullscreenchange':
case 'fullscreenerror':
case 'focus':
case 'hashchange':
case 'popstate':
case 'select':
case 'selectstart':
return DiscreteEventPriority;
case 'drag':
case 'dragenter':
case 'dragexit':
case 'dragleave':
case 'dragover':
case 'mousemove':
case 'mouseout':
case 'mouseover':
case 'pointermove':
case 'pointerout':
case 'pointerover':
case 'resize':
case 'scroll':
case 'touchmove':
case 'wheel':
// Not used by React but could be by user code: (fall through)
// React 不使用,但可能被用户代码使用: (继续执行)
case 'mouseenter':
case 'mouseleave':
case 'pointerenter':
case 'pointerleave':
return ContinuousEventPriority;
case 'message': {
// We might be in the Scheduler callback.
// Eventually this mechanism will be replaced by a check
// of the current priority on the native scheduler.
// 我们可能处于调度器回调中。
// 最终,这个机制将被替换为对本地调度器上当前优先级的检查。
const schedulerPriority = getCurrentSchedulerPriorityLevel();
switch (schedulerPriority) {
case ImmediateSchedulerPriority:
return DiscreteEventPriority;
case UserBlockingSchedulerPriority:
return ContinuousEventPriority;
case NormalSchedulerPriority:
case LowSchedulerPriority:
// TODO: Handle LowSchedulerPriority, somehow. Maybe the same lane as hydration.
// 待办:以某种方式处理 LowSchedulerPriority。也许使用与 hydration 相同的通道。
return DefaultEventPriority;
case IdleSchedulerPriority:
return IdleEventPriority;
default:
return DefaultEventPriority;
}
}
default:
return DefaultEventPriority;
}
}
十一、常量
1. 已启用
备注
源码中 68 - 69 行
// TODO: can we stop exporting these?
// 待办:我们能停止导出这些吗?
let _enabled: boolean = true;
十二、工具
1. 分发离散事件
备注
ReactSharedInternals由 ReactSharedInternals 提供getCurrentUpdatePriority()由 ReactDOMUpdatePriority 提供setCurrentUpdatePriority()由 ReactDOMUpdatePriority 提供
function dispatchDiscreteEvent(
domEventName: DOMEventName,
eventSystemFlags: EventSystemFlags,
container: EventTarget,
nativeEvent: AnyNativeEvent,
) {
const prevTransition = ReactSharedInternals.T;
ReactSharedInternals.T = null;
const previousPriority = getCurrentUpdatePriority();
try {
setCurrentUpdatePriority(DiscreteEventPriority);
dispatchEvent(domEventName, eventSystemFlags, container, nativeEvent);
} finally {
setCurrentUpdatePriority(previousPriority);
ReactSharedInternals.T = prevTransition;
}
}
2. 派发连续事件
备注
ReactSharedInternals由 ReactSharedInternals 提供getCurrentUpdatePriority()由 ReactDOMUpdatePriority 提供setCurrentUpdatePriority()由 ReactDOMUpdatePriority 提供
function dispatchContinuousEvent(
domEventName: DOMEventName,
eventSystemFlags: EventSystemFlags,
container: EventTarget,
nativeEvent: AnyNativeEvent,
) {
const prevTransition = ReactSharedInternals.T;
ReactSharedInternals.T = null;
const previousPriority = getCurrentUpdatePriority();
try {
setCurrentUpdatePriority(ContinuousEventPriority);
dispatchEvent(domEventName, eventSystemFlags, container, nativeEvent);
} finally {
setCurrentUpdatePriority(previousPriority);
ReactSharedInternals.T = prevTransition;
}
}