The classic event system is split into two chunks, binding events and dispatching events. In browsers, dispatching events is rarely done directly. Because creating a DOM event is very complex, different event objects correspond to different event constructors and pass parameters. Because the dispatch event basically triggers the user’s behavior, so let’s say we click on some element, and we bind the click event on top of it, and it triggers.

The React binding event is performed in JSX, in other words, the onXXX event of the props is collected when render is performed. In jQuery, people invented event delegate, which can bubble events on the Document or Window object, for unified monitoring, to achieve high-performance event system. We can now ignore IE8, so we can use event capture for non-bubbling events, event bubbling for bubbling events, and also listening on top objects.

The function trapBubbledEvent (topLevelType, element) {/ / by SiTuZhengMei trapEventForPluginEventSystem (element, topLevelType. false); } function trapCapturedEvent(topLevelType, element) { trapEventForPluginEventSystem(element, topLevelType, true); }Copy the code

The browser dom.addeventListener (type, fn, capture) also requires three parameters. Fn is a unified scheduling method, so one is omitted. While the bubbling and capture do distinguish by different methods, the actual work is trapEventForPluginEventSystem.

TrapEventForPluginEventSystem points three categories, according to event name DiscreteEvent, UserBlockingEvent, ContinuousEvent, distributed choose different events.

The function trapEventForPluginEventSystem (element, topLevelType, capture) {/ / by SiTuZhengMei var listener; switch (getEventPriority(topLevelType)) { case DiscreteEvent: listener = dispatchDiscreteEvent.bind(null, topLevelType, PLUGIN_EVENT_SYSTEM); break; case UserBlockingEvent: listener = dispatchUserBlockingUpdate.bind(null, topLevelType, PLUGIN_EVENT_SYSTEM); break; Case ContinuousEvent: //by Stuart default: listener = dispatchEvent.bind(null, topLevelType, PLUGIN_EVENT_SYSTEM); break; } var rawEventName = getRawEventName(topLevelType); if (capture) { addEventCaptureListener(element, rawEventName, listener); } else { addEventBubbleListener(element, rawEventName, listener); }}Copy the code

DiscreteEvent indicates a DiscreteEvent. For example, blur, focus, click, submit, touchStart. These events are triggered discreetly.

UserBlockingEvent UserBlockingEvent. Examples include touchMove, mouseMove, Scroll, drag, dragOver, etc. These events’ block ‘user interaction.

ContinuousEvent Continuous events. Examples are load, error, loadStart, abort, animationEnd. This has the highest priority, which means that they should be executed immediately and synchronously. That’s what Continuous means.

There is a long table in the source code to classify all common events

var eventTuples = [// Discrete events
[TOP_BLUR, 'blur', DiscreteEvent], [TOP_CANCEL, 'cancel', DiscreteEvent], [TOP_CLICK, 'click', DiscreteEvent], [TOP_CLOSE, 'close', DiscreteEvent], [TOP_CONTEXT_MENU, 'contextMenu', DiscreteEvent], [TOP_COPY, 'copy', DiscreteEvent], [TOP_CUT, 'cut', DiscreteEvent], [TOP_AUX_CLICK, 'auxClick', DiscreteEvent], [TOP_DOUBLE_CLICK, 'doubleClick', DiscreteEvent], [TOP_DRAG_END, 'dragEnd', DiscreteEvent], [TOP_DRAG_START, 'dragStart', DiscreteEvent], [TOP_DROP, 'drop', DiscreteEvent], [TOP_FOCUS, 'focus', DiscreteEvent], [TOP_INPUT, 'input', DiscreteEvent], [TOP_INVALID, 'invalid', DiscreteEvent], [TOP_KEY_DOWN, 'keyDown', DiscreteEvent], [TOP_KEY_PRESS, 'keyPress', DiscreteEvent], [TOP_KEY_UP, 'keyUp', DiscreteEvent], [TOP_MOUSE_DOWN, 'mouseDown', DiscreteEvent], [TOP_MOUSE_UP, 'mouseUp', DiscreteEvent], [TOP_PASTE, 'paste', DiscreteEvent], [TOP_PAUSE, 'pause', DiscreteEvent], [TOP_PLAY, 'play', DiscreteEvent], [TOP_POINTER_CANCEL, 'pointerCancel', DiscreteEvent], [TOP_POINTER_DOWN, 'pointerDown', DiscreteEvent], [TOP_POINTER_UP, 'pointerUp', DiscreteEvent], [TOP_RATE_CHANGE, 'rateChange', DiscreteEvent], [TOP_RESET, 'reset', DiscreteEvent], [TOP_SEEKED, 'seeked', DiscreteEvent], [TOP_SUBMIT, 'submit', DiscreteEvent], [TOP_TOUCH_CANCEL, 'touchCancel', DiscreteEvent], [TOP_TOUCH_END, 'touchEnd', DiscreteEvent], [TOP_TOUCH_START, 'touchStart', DiscreteEvent], [TOP_VOLUME_CHANGE, 'volumeChange', DiscreteEvent], // User-blocking events
[TOP_DRAG, 'drag', UserBlockingEvent], [TOP_DRAG_ENTER, 'dragEnter', UserBlockingEvent], [TOP_DRAG_EXIT, 'dragExit', UserBlockingEvent], [TOP_DRAG_LEAVE, 'dragLeave', UserBlockingEvent], [TOP_DRAG_OVER, 'dragOver', UserBlockingEvent], [TOP_MOUSE_MOVE, 'mouseMove', UserBlockingEvent], [TOP_MOUSE_OUT, 'mouseOut', UserBlockingEvent], [TOP_MOUSE_OVER, 'mouseOver', UserBlockingEvent], [TOP_POINTER_MOVE, 'pointerMove', UserBlockingEvent], [TOP_POINTER_OUT, 'pointerOut', UserBlockingEvent], [TOP_POINTER_OVER, 'pointerOver', UserBlockingEvent], [TOP_SCROLL, 'scroll', UserBlockingEvent], [TOP_TOGGLE, 'toggle', UserBlockingEvent], [TOP_TOUCH_MOVE, 'touchMove', UserBlockingEvent], [TOP_WHEEL, 'wheel', UserBlockingEvent], // Continuous events
[TOP_ABORT, 'abort', ContinuousEvent], [TOP_ANIMATION_END, 'animationEnd', ContinuousEvent], [TOP_ANIMATION_ITERATION, 'animationIteration', ContinuousEvent], [TOP_ANIMATION_START, 'animationStart', ContinuousEvent], [TOP_CAN_PLAY, 'canPlay', ContinuousEvent], [TOP_CAN_PLAY_THROUGH, 'canPlayThrough', ContinuousEvent], [TOP_DURATION_CHANGE, 'durationChange', ContinuousEvent], [TOP_EMPTIED, 'emptied', ContinuousEvent], [TOP_ENCRYPTED, 'encrypted', ContinuousEvent], [TOP_ENDED, 'ended', ContinuousEvent], [TOP_ERROR, 'error', ContinuousEvent], [TOP_GOT_POINTER_CAPTURE, 'gotPointerCapture', ContinuousEvent], [TOP_LOAD, 'load', ContinuousEvent], [TOP_LOADED_DATA, 'loadedData', ContinuousEvent], [TOP_LOADED_METADATA, 'loadedMetadata', ContinuousEvent], [TOP_LOAD_START, 'loadStart', ContinuousEvent], [TOP_LOST_POINTER_CAPTURE, 'lostPointerCapture', ContinuousEvent], [TOP_PLAYING, 'playing', ContinuousEvent], [TOP_PROGRESS, 'progress', ContinuousEvent], [TOP_SEEKING, 'seeking', ContinuousEvent], [TOP_STALLED, 'stalled', ContinuousEvent], [TOP_SUSPEND, 'suspend', ContinuousEvent], [TOP_TIME_UPDATE, 'timeUpdate', ContinuousEvent], [TOP_TRANSITION_END, 'transitionEnd', ContinuousEvent], [TOP_WAITING, 'waiting', ContinuousEvent]];
Copy the code

AddEventBubbleListener and addEventCaptureListener are simple encapsulation of DOM’s addEvenListener. Note, however, that both methods are implemented using injection. In other words, in React-Native, the corresponding implementation is different because the packaged files are different

function addEventBubbleListener(element, eventType, listener) { element.addEventListener(eventType, listener, false); } function addEventCaptureListener(element, eventType, listener) {element. AddEventListener (eventType, eventType, listener) listener, true); //by Stuart}Copy the code

And then we finally had a chance to see dispatchDiscreteEvent to dispatchUserBlockingUpdate dispatchEvent.

function dispatchDiscreteEvent(topLevelType, eventSystemFlags, nativeEvent) { flushDiscreteUpdatesIfNeeded(nativeEvent.timeStamp); discreteUpdates(dispatchEvent, topLevelType, eventSystemFlags, nativeEvent); } / / by SiTuZhengMei function dispatchUserBlockingUpdate (topLevelType eventSystemFlags, nativeEvent) { runWithPriority(UserBlockingPriority, dispatchEvent.bind(null, topLevelType, eventSystemFlags, nativeEvent)); }Copy the code

Distributed discrete event is divided into two parts, the first is flushDiscreteUpdatesIfNeeded, you are tracking in flushDiscreteUpdates, it will be done before accumulate DiscreteEvent and useEffect callback. The second is the discreteUpdates, it could be the React of superposition of scheduling a DiscreteEventContext context, and perform runWithPriority, then it seems like dispatchUserBlockingUpdate to it I’m just doing a pre-processing.

function discreteUpdates(fn, a, b, c) { var prevExecutionContext = executionContext; executionContext |= DiscreteEventContext; try { // Should this return runWithPriority(UserBlockingPriority, fn.bind(null, a, b, c)); } finally { executionContext = prevExecutionContext; if (executionContext === NoContext) { // Flush the immediate callbacks that were scheduled during this batch flushSyncCallbackQueue(); }}}Copy the code

RunWithPriority has two major effects on the scheduler. One is the expirationTime property of a Fiber node, which involves the DOM refresh of fiber, and the other is the execution of a callback on a Fiber node (setState, forceUpdate, Hooks), which are passed on to the scheduleCallback method for processing. The scheduleCallback acts as a setTimeout and delays the execution of events based on the priorityLevel. ScheduleCallback takes a closer look at this

Github.com/facebook/re…

PriorityLevel is immediately converted to callbackPriority, which defaults to NoPriority 90. And then as it runs, it becomes the following five.

ImmediatePriority 99 IMMEDIATE_PRIORITY_TIMEOUT=-1;

UserBlockingPriority 98 USER_BLOCKING_PRIORITY=250

NormalPriority 97 NORMAL_PRIORITY_TIMEOUT=5000

LowPriority 96 LOW_PRIORITY_TIMEOUT=10000

IdlePriority 85 LOW_PRIORITY_TIMEOUT=10000

Every fiber is given a expirationTime property that is larger than the number of milliseconds. Now -Fiber. ExpirationTime <= 0. Fiber should be updated and its priorityLevel should be changed to ImmediatePriority. Otherwise, an effect is created to record the user’s actions (such as updating a property or deleting it).

Ok, so we know the importance of runWithPriority, so we need to get the other priorityLevel values.

FlushSync contains the runWithPriority(FN) logic for flushControlled, deferredUpdates and flushSync. SyncUpdates and flushSyncCallback contain the logic of runWithPriority(ImmediatePriority, FN).

In addition, SuspenseComponent also affects the calculation of expirationTime (assigned LOW_PRIORITY_EXPIRATION by default), Finally, through the method of internal inferPriorityFromExpirationTime priorityLevel value calculation.

The React version is 16.10.2

Conclusion: Years ago, when people talked about fiber, they only vaguely associated the words Time slicing and Suspense. After several iterations, fiber was finally revealed. React time slice is just a kind of update. In essence, every Fiber expirationTime is determined by fiber expirationTime, and Fiber expirationTime comes from priorityLevel, and priorityLevel comes from user UI operations. Different events lead to three different prioritylevels. Hovering only brings a fourth priorityLevel — LowPriority — to a fiber. A fifth priorityLevel, IdlePriority, occurs when the user code is caught because of a problem.