Lebenszyklus- und Ereignissystem im Reaktionsquellcode

Was ich in diesem Kapitel mit Ihnen besprechen möchte, ist ja Reactund nein .生命周期事件系统

Das Kompilierungsergebnis von jsx

Da ich oben auch jsxdie v17Kompilierungsergebnisse erwähnt habe, werden sie mit Ausnahme 标签名anderer Tags 属性(z. B. class) und 事件(z. B. clickEreignisse) alle _jsxRuntime.jsxim zweiten Parameter der Funktion platziert. In Form von Leistung key:valuewerden wir hier mehrere Probleme haben.

  • reactWoher wissen Sie, was der Funktionskörper (Event-Handler) ist?
  • reactIn welcher Phase werden diese Ereignisse behandelt?

Lassen Sie uns hier zunächst einen Punkt festhalten. Schauen wir uns zunächst an, wie Reactder vollständige Lebenszyklus einer vollständigen Anwendung aussieht. Wir alle wissen, dass er Reactin 类组件und 函数组件einen Teil der Lebenszyklusfunktionen der beiden Komponenten unterteilt ist. 发生了一些变化Hier werde ich den analysieren zwei Komponenten getrennt dar. Erklären Sie den Lebenszyklus von Komponenten.

Lebenszyklus der Reaktionskomponente

Ausführungsreihenfolge bei montierten Komponenten

Denn beim _jsxRuntime.jsxKompilieren jsxvon Objekten führen wir eine Verarbeitung defaultPropsund propTypestatische Typprüfung durch. Das ist also auch ein Lebenszyklus. ClassKomponenten sind getrennt, und dieser Konstruktor wird schrittweise ausgeführt.Ich constructorhabe einige Nachforschungen angestellt.Ist dies einzigartig für Klassenkomponenten oder ist es einzigartig? Später fand ich heraus, dass dies einzigartig ist , wie ist dieser Satz zu verstehen?mountconstructorclassconstructorclass

  • Es wird im Buch "Re-learning ES6" erwähnt: ES6Das Konzept einer Klasse wird in hinzugefügt, eine Klasse muss eine constructorMethode haben, wenn es keine explizite Definition in der Klasse gibt, constructorwird standardmäßig eine leere Methode hinzugefügt. Für das ReactClassComponentSprechen constructorbesteht die erforderliche Funktion darin, stateEreignisse zu initialisieren und zu binden. Der andere Punkt ist, dass constructorsie aufgerufen werden muss, nachdem sie deklariert wurde super. Wir verwenden sie im Allgemeinen zum Empfangen und propsÜbergeben. Wenn Sie es nicht schreiben constructor, funktioniert es nicht Wenn Sie es in verwenden möchten , müssen Sie propsnatürlich den Empfang verwenden.constructorpropssuper
  • Für Klassenkomponenten constructorkann es also als Lebenszyklushaken betrachtet werden.

getDerivedStateFromPropsWird aufgerufen, bevor die Rendermethode aufgerufen wird, und wird beim anfänglichen Mounten und bei nachfolgenden Aktualisierungen aufgerufen. Es sollte ein Objekt in den Aktualisierungszustand zurückversetzen, wenn es nullzurückgibt , wird nichts aktualisiert.

renderWenn es aufgerufen wird, prüft es this.propsund this.stateauf Änderungen und gibt einen der folgenden Typen zurück:

  • Elemente reagieren . Wird normalerweise über JSX erstellt. Beispielsweise wird <div />es von React als DOM-Knoten gerendert <MyComponent />und von React als benutzerdefinierte Komponente gerendert, unabhängig davon, ob es <div />sich <MyComponent />um ein React-Element handelt oder nicht.
  • Array oder Fragmente . Ermöglicht der Rendermethode, mehrere Elemente zurückzugeben.
  • Portale . Es ist möglich, untergeordnete Knoten in verschiedene DOM-Teilbäume zu rendern.
  • Zeichenfolge oder numerischer Typ . Sie werden als Textknoten im DOM gerendert.
  • Boolesch odernull . Es wird nichts gerendert. (Wird hauptsächlich verwendet, um Muster zu unterstützen, die test && <Child />zurückgeben , wobei test ein boolescher Wert ist.)

componentDidMount()Wird sofort aufgerufen, nachdem die Komponente gemountet (in den DOM-Baum eingefügt) wurde. Die Initialisierung, die von DOM-Knoten abhängt, sollte hier stehen. Dies eignet sich zum Senden asynchroner Anfragen.

Ausführungsreihenfolge beim Aktualisieren von Komponenten

getDerivedStateFromProps=> shouldComponentUpdate()=> render()=> getSnapshotBeforeUpdate()=>componentDidUpdate()

  • Es shouldComponentUpdatewird auch als Hook zur Leistungsoptimierung bezeichnet. Seine Funktion besteht darin, die beiden Updates zu vergleichen stateoder propsob sie sich geändert haben, und zu entscheiden, ob die aktuelle Komponente aktualisiert werden soll. Die Vergleichsmethode ist 浅比较, dass sie zuvor erwähnt wurde und hier nicht wiederholt wird.
  • Und die getSnapshotBeforeUpdateFunktion wird vor der letzten gerenderten Ausgabe aufgerufen (an den DOMKnoten übermittelt). Es ermöglicht Komponenten, einige Informationen DOMvon ihnen . Jeder Rückgabewert dieser Lebenszyklusmethode wird als Argument an übergeben componentDidUpdate().
  • componentDidUpdate()wird direkt nach dem Update aufgerufen. 不会执行Diese Methode wird zuerst gerendert .

Ausführungsreihenfolge bei der Deinstallation von Komponenten

componentWillUnmount()卸载Wird direkt vor der Komponente aufgerufen 销毁. Führen Sie bei dieser Methode die erforderlichen Bereinigungsvorgänge durch, z. B. cleanup timerusw.取消网络请求

Reihenfolge der Komponentenausführung, wenn ein Fehler auftritt

getDerivedStateFromError=> componentDidCatchIn Bezug auf diese beiden Haken können die Schüler selbst auf die offizielle Website wechseln.

Natürlich ist das Obige nur ClassComponentdie Ausführungsreihenfolge des Lebenszyklus, und in der neuen Version von React componentDidMountwurden , componentDidUpdate, , , gelöscht componentWillUnMountund durch useEffect, ersetzt useLayoutEffect. Wer genau hat die drei ersetzt? Ich habe dieses Problem bereits in der React-Quellcode-Analyseserie (8) erläutert – das Prinzip der tiefgehenden Hooks, daher werde ich es hier nicht wiederholen.

Nun zur Beantwortung der ersten Frage: Woher weiß die Reaktion, was der Körper der Funktion ist? Diese Frage ist eigentlich eine sehr gute: babelNach dem Parsen werden Sie jsxnur darauf achten {事件名:函数名}, aber jedes Ereignis muss registriert und gebunden und dann durch das Ereignis ausgelöst werden, um den Funktionskörper der gebundenen Funktion auszuführen. Um diese Art von Problem zu erklären, müssen wir uns noch die spezifische Implementierung im Quellcode ansehen.

listenToAllSupportedEvents

Wir haben in der React-Quellcode-Analysereihe (2) den Erstellungs- und Aktualisierungsprozess der Initialisierungskomponente erwähnt. rootFiberNachdem FiberRootdie Erstellung abgeschlossen ist, müssen wir ein Ereignis erstellen. Die Eingabefunktion zum Erstellen eines Ereignisses ist listenToAllSupportedEvents.

// packages/react-dom/src/events/DOMPluginEventSystem.js
export function listenToAllSupportedEvents(rootContainerElement: EventTarget) {if (enableEagerRootListeners) { // enableEagerRootListeners默认值为false// listeningMarker就是一个随机数+字符串,作为唯一值if (rootContainerElement[listeningMarker]) {...return;}rootContainerElement[listeningMarker] = true;// 遍历allNativeEvents的所有事件allNativeEvents.forEach(domEventName => {// 如果不是委托事件,没有冒泡阶段// nonDelegatedEvents全部媒体事件,if (!nonDelegatedEvents.has(domEventName)) {listenToNativeEvent(domEventName,false,((rootContainerElement: any): Element),null,);}// 有冒泡阶段listenToNativeEvent(domEventName,true,((rootContainerElement: any): Element),null,);});}
}

//listeningMarker
// 唯一标识
const listeningMarker ='_reactListening' +Math.random().toString(36).slice(2); 

Wir müssen darauf achten, was allNativeEventshier steht, allNativeEventswas sich im Quellcode als SetStruktur widerspiegelt, die Ereignisnamen speichert:

export const allNativeEvents: Set<DOMEventName> = new Set(); 

Mal sehen listenToNativeEvent, was als nächstes passiert ist.

listenToNativeEvent

export function listenToNativeEvent( domEventName: DOMEventName,// 事件名isCapturePhaseListener: boolean, // 根据上个函数,这里应该是确定是是能够冒泡的事件rootContainerElement: EventTarget,targetElement: Element | null,eventSystemFlags?: EventSystemFlags = 0, // 事件标记 ): void {let target = rootContainerElement;//如果是selectionchange事件,加到dom上if (domEventName === 'selectionchange' &&(rootContainerElement: any).nodeType !== DOCUMENT_NODE) {target = (rootContainerElement: any).ownerDocument;}if (targetElement !== null &&!isCapturePhaseListener &&nonDelegatedEvents.has(domEventName) // 非冒泡事件) { ...//滚动事件不冒泡if (domEventName !== 'scroll') {return;}eventSystemFlags |= IS_NON_DELEGATED; // is_non_delegated 不是委托事件target = targetElement;}//获取dom上绑定的事件名数组 Set[] || const listenerSet = getEventListenerSet(target);// 处理事件名为捕获阶段与冒泡阶段 Set[click_bubble]const listenerSetKey = getListenerSetKey(domEventName,isCapturePhaseListener,);// 把没有打过的IS_CAPTURE_PHASE的符合条件的事件,打上标签if (!listenerSet.has(listenerSetKey)) {if (isCapturePhaseListener) {// 打上捕获的标签eventSystemFlags |= IS_CAPTURE_PHASE;}// 往节点上添加事件绑定addTrappedEventListener(target,domEventName,eventSystemFlags,isCapturePhaseListener,);// 往listenerSet中添加事件名listenerSet.add(listenerSetKey);}
}

//getEventListenerSet
export function getEventListenerSet(node: EventTarget): Set<string> {let elementListenerSet = (node: any)[internalEventHandlersKey];if (elementListenerSet === undefined) {// 创建一个Set来存放事件名elementListenerSet = (node: any)[internalEventHandlersKey] = new Set();}return elementListenerSet;
}

// getListenerSetKey
export function getListenerSetKey( domEventName: DOMEventName,capture: boolean, ): string {// capture捕获,bubble冒泡return `${domEventName}__${capture ? 'capture' : 'bubble'}`;
}

// addTrappedEventListener
function addTrappedEventListener( targetContainer: EventTarget, // 容器domEventName: DOMEventName, // 事件名eventSystemFlags: EventSystemFlags, //事件名标识isCapturePhaseListener: boolean, // 事件委托isDeferredListenerForLegacyFBSupport?: boolean, ) {// 创建具有优先级的事件监听函数,返回值为functionlet listener = createEventListenerWrapperWithPriority(targetContainer,domEventName,eventSystemFlags,);...targetContainer =enableLegacyFBSupport && isDeferredListenerForLegacyFBSupport? (targetContainer: any).ownerDocument: targetContainer;let unsubscribeListener;...// 区分捕获、冒泡 通过node.addEventListener绑定事件到节点上if (isCapturePhaseListener) {if (isPassiveListener !== undefined) {unsubscribeListener = addEventCaptureListenerWithPassiveFlag(targetContainer,domEventName,listener,isPassiveListener,);} else {unsubscribeListener = addEventCaptureListener(targetContainer,domEventName,listener,);}} else {if (isPassiveListener !== undefined) {unsubscribeListener = addEventBubbleListenerWithPassiveFlag(targetContainer,domEventName,listener,isPassiveListener,);} else {unsubscribeListener = addEventBubbleListener(targetContainer,domEventName,listener,);}}
}

// createEventListenerWrapperWithPriority
export function createEventListenerWrapperWithPriority( targetContainer: EventTarget, // 容器domEventName: DOMEventName, // 事件名eventSystemFlags: EventSystemFlags, //标识 ): Function {// 获取事件Map里面已经标记好的优先级const eventPriority = getEventPriorityForPluginSystem(domEventName);let listenerWrapper;// 根据优先级不同绑定不同的执行函数switch (eventPriority) {//离散事件case DiscreteEvent:listenerWrapper = dispatchDiscreteEvent;break;// 用户交互阻塞渲染的事件 case UserBlockingEvent:listenerWrapper = dispatchUserBlockingUpdate;break;// 其他事件case ContinuousEvent:// 默认事件default:listenerWrapper = dispatchEvent;break;}return listenerWrapper.bind(null,domEventName,eventSystemFlags,targetContainer,);
} 

Hier konzentrieren wir uns darauf, getEventPriorityForPluginSystemhier die Priorität zu bekommen.Haben Sie eine Frage React?Wir wissen, dass internen Ereignissen Reactdefinitiv Priorität eingeräumt wird, aber was Reactist beispielsweise mit Nicht-Ereignissen 原生事件, wie werden ihre Prioritäten festgelegt? Keine Sorge, wir werden sehen, wenn wir einen Blick darauf werfen.

getEventPriorityForPluginSystem

export function getEventPriorityForPluginSystem( domEventName: DOMEventName, ): EventPriority {// 通过事件名获取优先级const priority = eventPriorities.get(domEventName);// ContinuousEvent为默认优先级 return priority === undefined ? ContinuousEvent : priority;
}

//eventPriorities
const eventPriorities = new Map(); 

eventPrioritiesEs ist selbst eine Map-Struktur, und wir können die durchgeführten Operationen an zwei Stellen finden eventPriorities.set().

// packages/react-dom/src/events/DOMEventProperties.js
function setEventPriorities( eventTypes: Array<DOMEventName>,priority: EventPriority, ): void {for (let i = 0; i < eventTypes.length; i++) {// 往eventPriorities添加优先级eventPriorities.set(eventTypes[i], priority);}
}

//registerSimplePluginEventsAndSetTheirPriorities
function registerSimplePluginEventsAndSetTheirPriorities( eventTypes: Array<DOMEventName | string>,priority: EventPriority, ): void {for (let i = 0; i < eventTypes.length; i += 2) {const topEvent = ((eventTypes[i]: any): DOMEventName);const event = ((eventTypes[i + 1]: any): string);const capitalizedEvent = event[0].toUpperCase() + event.slice(1);// 改变事件名 click => onClickconst reactName = 'on' + capitalizedEvent;// 往eventPriorities添加优先级eventPriorities.set(topEvent, priority);topLevelEventsToReactNames.set(topEvent, reactName);// 注册捕获阶段,冒泡阶段的事件registerTwoPhaseEvent(reactName, [topEvent]);}
} 

Dies bedeutet, dass die Prioritätsverarbeitung in diesen beiden Funktionen durchgeführt wurde, sodass wir uns ansehen können, wo diese beiden Funktionen aufgerufen werden. Wir haben festgestellt, dass in der Funktion registerSimpleEventsdiese beiden Funktionen eventPrioritiesausgeführt wurden, und ihr Priorität hinzugefügt.

// packages/react-dom/src/events/DOMEventProperties.js
export function registerSimpleEvents() {// 处理离散事件优先级registerSimplePluginEventsAndSetTheirPriorities(discreteEventPairsForSimpleEventPlugin,DiscreteEvent,);// 处理用户阻塞事件优先级registerSimplePluginEventsAndSetTheirPriorities(userBlockingPairsForSimpleEventPlugin,UserBlockingEvent,);// 处理默认事件优先级registerSimplePluginEventsAndSetTheirPriorities(continuousPairsForSimpleEventPlugin,ContinuousEvent,);// 处理其他事件优先级setEventPriorities(otherDiscreteEvents, DiscreteEvent);
} 

Sie können sehen, dass der obige Code viele enthält . Der PluginCode lautet wie folgt:

const discreteEventPairsForSimpleEventPlugin = [('cancel': DOMEventName), 'cancel',('click': DOMEventName), 'click',('close': DOMEventName), 'close',('contextmenu': DOMEventName), 'contextMenu',('copy': DOMEventName), 'copy',('cut': DOMEventName), 'cut',('auxclick': DOMEventName), 'auxClick',('dblclick': DOMEventName), 'doubleClick', // Careful!('dragend': DOMEventName), 'dragEnd',('dragstart': DOMEventName), 'dragStart',('drop': DOMEventName), 'drop',('focusin': DOMEventName), 'focus', // Careful!('focusout': DOMEventName), 'blur', // Careful!('input': DOMEventName), 'input',('invalid': DOMEventName), 'invalid',('keydown': DOMEventName), 'keyDown',('keypress': DOMEventName), 'keyPress',('keyup': DOMEventName), 'keyUp',('mousedown': DOMEventName), 'mouseDown',('mouseup': DOMEventName), 'mouseUp',('paste': DOMEventName), 'paste',('pause': DOMEventName), 'pause',('play': DOMEventName), 'play',('pointercancel': DOMEventName), 'pointerCancel',('pointerdown': DOMEventName), 'pointerDown',('pointerup': DOMEventName), 'pointerUp',('ratechange': DOMEventName), 'rateChange',('reset': DOMEventName), 'reset',('seeked': DOMEventName), 'seeked',('submit': DOMEventName), 'submit',('touchcancel': DOMEventName), 'touchCancel',('touchend': DOMEventName), 'touchEnd',('touchstart': DOMEventName), 'touchStart',('volumechange': DOMEventName), 'volumeChange',
];

const otherDiscreteEvents: Array<DOMEventName> = ['change','selectionchange','textInput','compositionstart','compositionend','compositionupdate',
];

const userBlockingPairsForSimpleEventPlugin: Array<string | DOMEventName> = [('drag': DOMEventName), 'drag',('dragenter': DOMEventName), 'dragEnter',('dragexit': DOMEventName), 'dragExit',('dragleave': DOMEventName), 'dragLeave',('dragover': DOMEventName), 'dragOver',('mousemove': DOMEventName), 'mouseMove',('mouseout': DOMEventName), 'mouseOut',('mouseover': DOMEventName), 'mouseOver',('pointermove': DOMEventName), 'pointerMove',('pointerout': DOMEventName), 'pointerOut',('pointerover': DOMEventName), 'pointerOver',('scroll': DOMEventName), 'scroll',('toggle': DOMEventName), 'toggle',('touchmove': DOMEventName), 'touchMove',('wheel': DOMEventName), 'wheel',
];

const continuousPairsForSimpleEventPlugin: Array<string | DOMEventName> = [('abort': DOMEventName), 'abort',(ANIMATION_END: DOMEventName), 'animationEnd',(ANIMATION_ITERATION: DOMEventName), 'animationIteration',(ANIMATION_START: DOMEventName), 'animationStart',('canplay': DOMEventName), 'canPlay',('canplaythrough': DOMEventName), 'canPlayThrough',('durationchange': DOMEventName), 'durationChange',('emptied': DOMEventName), 'emptied',('encrypted': DOMEventName), 'encrypted',('ended': DOMEventName), 'ended',('error': DOMEventName), 'error',('gotpointercapture': DOMEventName), 'gotPointerCapture',('load': DOMEventName), 'load',('loadeddata': DOMEventName), 'loadedData',('loadedmetadata': DOMEventName), 'loadedMetadata',('loadstart': DOMEventName), 'loadStart',('lostpointercapture': DOMEventName), 'lostPointerCapture',('playing': DOMEventName), 'playing',('progress': DOMEventName), 'progress',('seeking': DOMEventName), 'seeking',('stalled': DOMEventName), 'stalled',('suspend': DOMEventName), 'suspend',('timeupdate': DOMEventName), 'timeUpdate',(TRANSITION_END: DOMEventName), 'transitionEnd',('waiting': DOMEventName), 'waiting',
]; 

In der registerSimplePluginEventsAndSetTheirPrioritiesFunktion haben wir das Registrierungsereignis gefunden registerTwoPhaseEvent. Lassen Sie uns nun herausfinden, wie es registriert wird.

registerTwoPhaseEvent

export function registerTwoPhaseEvent( registrationName: string, // 注册事件reactNamedependencies: Array<DOMEventName>, // 依赖 ): void {registerDirectEvent(registrationName, dependencies);registerDirectEvent(registrationName + 'Capture', dependencies);
} 

registerDirectEvent

// Mapping from registration name to event name
export const registrationNameDependencies = {};

export function registerDirectEvent( registrationName: string, //react事件名onClickdependencies: Array<DOMEventName>, // 依赖 ) {...// 以react事件名为key,dependencies为value的map对象registrationNameDependencies[registrationName] = dependencies;if (__DEV__) {...}// 遍历依赖,把每一项加入到allNativeEvents中去for (let i = 0; i < dependencies.length; i++) {allNativeEvents.add(dependencies[i]);}
} 

Es wird gesagt, dass es allNativeEventssich um einen Speicherereignisnamen handelt Set, fügen Sie ihn hier hinzu 事件名, und Sie sind fertig 事件注册. Es ist noch nicht vorbei, oben wurde erwähnt, dass die Eventregistrierung an das Event gebunden ist, aber wenn der Benutzer klickt, wie soll es ausgelöst werden? Im obigen Code generiert jedes Ereignis nach Erhalt der Priorität eine entsprechend der aktuellen Priorität listenerWrapper, die listenerWrapperdie entsprechende Ereignis-Trigger-Bindungsfunktion ist. dispatchDiscreteEvent, dispatchUserBlockingUpdate, und die dispatchEventdrei Funktionen werden alle durch bind ausgeführt.Wir alle wissen, dass die durch bind gebundene Funktion eine neue Funktion zurückgibt und nicht sofort ausgeführt wird. Also müssen wir auch sehen, was sein Eintrag ist.

  • this:null
  • argments: domEventName: Ereignisname, eventSystemFlags: Ereignistyp-Tag, targetContainer: Zielcontainer.

Versandereignis

Denn was auch immer es ist dispatchDiscreteEvent, dispatchUserBlockingUpdatees wird am Ende ausgeführt dispatchEvent, also können wir uns seine Implementierung ansehen.

// packages/react-dom/src/events/ReactDOMEventListener.js
export function dispatchEvent( domEventName: DOMEventName, // 事件名eventSystemFlags: EventSystemFlags, // 事件类型标记targetContainer: EventTarget, // 目标容器nativeEvent: AnyNativeEvent, // native事件 ): void {...// 如果被阻塞了,尝试调度事件 并返回挂载的实例或者容器const blockedOn = attemptToDispatchEvent(domEventName,eventSystemFlags,targetContainer,nativeEvent,);if (blockedOn === null) {// We successfully dispatched this event....return;}...// 调度事件,触发事件dispatchEventForPluginEventSystem(domEventName,eventSystemFlags,nativeEvent,null,targetContainer,);
}

// dispatchEventForPluginEventSystem
export function dispatchEventForPluginEventSystem( domEventName: DOMEventName,eventSystemFlags: EventSystemFlags,nativeEvent: AnyNativeEvent,targetInst: null | Fiber,targetContainer: EventTarget, ): void {...//批量更新事件 batchedEventUpdates(() =>dispatchEventsForPlugins(domEventName,eventSystemFlags,nativeEvent,ancestorInst,targetContainer,),);
}

// batchedEventUpdates
export function batchedEventUpdates(fn, a, b) {...isBatchingEventUpdates = true;try {// fn : ()=>dispatchEventsForPlugins//(domEventName,eventSystemFlags,ativeEvent,ancestorInst,targetContainer,),// a: undefined// b: undefinedreturn batchedEventUpdatesImpl(fn, a, b); // batchedEventUpdatesImpl(fn, a, b) =>// Defaults// let batchedUpdatesImpl = function(fn, bookkeeping) {// return fn(bookkeeping); 执行dispatchEventsForPlugins
};} finally {...}
} 

DispatchEventsForPlugins

function dispatchEventsForPlugins( domEventName: DOMEventName,eventSystemFlags: EventSystemFlags,nativeEvent: AnyNativeEvent,targetInst: null | Fiber,targetContainer: EventTarget, ): void {const nativeEventTarget = getEventTarget(nativeEvent);const dispatchQueue: DispatchQueue = [];//创建合成事件,遍历fiber链表,将会触发的事件加入到dispatchQueue中extractEvents(dispatchQueue,domEventName,targetInst,nativeEvent,nativeEventTarget,eventSystemFlags,targetContainer,);//触发时间队列,执行事件processDispatchQueue(dispatchQueue, eventSystemFlags);
}

//extractEvents
function extractEvents( dispatchQueue: DispatchQueue,domEventName: DOMEventName,targetInst: null | Fiber,nativeEvent: AnyNativeEvent,nativeEventTarget: null | EventTarget,eventSystemFlags: EventSystemFlags,targetContainer: EventTarget, ) {...let from;let to;...const leave = new SyntheticEventCtor(leaveEventType,eventTypePrefix + 'leave',from,nativeEvent,nativeEventTarget,);leave.target = fromNode;leave.relatedTarget = toNode;let enter: KnownReactSyntheticEvent | null = null;...accumulateEnterLeaveTwoPhaseListeners(dispatchQueue, leave, enter, from, to);
}

//accumulateEnterLeaveTwoPhaseListeners
export function accumulateEnterLeaveTwoPhaseListeners( dispatchQueue: DispatchQueue,leaveEvent: KnownReactSyntheticEvent,enterEvent: null | KnownReactSyntheticEvent,from: Fiber | null,to: Fiber | null, ): void {const common = from && to ? getLowestCommonAncestor(from, to) : null;if (from !== null) {accumulateEnterLeaveListenersForEvent(dispatchQueue,leaveEvent,from,common,false,);}if (to !== null && enterEvent !== null) {accumulateEnterLeaveListenersForEvent(dispatchQueue,enterEvent,to,common,true,);}
}

// accumulateEnterLeaveListenersForEvent
function accumulateEnterLeaveListenersForEvent( dispatchQueue: DispatchQueue,event: KnownReactSyntheticEvent,target: Fiber,common: Fiber | null,inCapturePhase: boolean, ): void {// 获取注册的事件名const registrationName = event._reactName;// 事件处理函数容器const listeners: Array<DispatchListener> = [];//节点实例let instance = target;// 遍历fiber,获取fiber上的事件对应的事件处理函数while (instance !== null) {if (instance === common) {break;}const {alternate, stateNode, tag} = instance;if (alternate !== null && alternate === common) {break;}if (tag === HostComponent && stateNode !== null) {const currentTarget = stateNode;// 根据捕获阶段,还是冒泡阶段处理不同的函数逻辑if (inCapturePhase) {const captureListener = getListener(instance, registrationName);if (captureListener != null) {// 加入到listeners中// instance:当前fiebr实例// currentTarget:当前domlisteners.unshift(createDispatchListener(instance, captureListener, currentTarget),);}} else if (!inCapturePhase) {// 冒泡const bubbleListener = getListener(instance, registrationName);if (bubbleListener != null) {// 加入到listeners中listeners.push(createDispatchListener(instance, bubbleListener, currentTarget),);}}}// 当前fiber实例的父级instance = instance.return;}if (listeners.length !== 0) {// 把事件、事件处理函数全部推到dispatchQueue中dispatchQueue.push({event, listeners});}
}
// processDispatchQueue
export function processDispatchQueue( dispatchQueue: DispatchQueue, // 事件队列eventSystemFlags: EventSystemFlags, // 事件类型标记 ): void {const inCapturePhase = (eventSystemFlags & IS_CAPTURE_PHASE) !== 0;for (let i = 0; i < dispatchQueue.length; i++) {const {event, listeners} = dispatchQueue[i];// 执行事件,完成触发processDispatchQueueItemsInOrder(event, listeners, inCapturePhase);//event system doesn't use pooling.}// This would be a good time to rethrow if any of the event handlers threw.rethrowCaughtError();
} 

An dieser Stelle ist Reactdie Analyse des Ereignissystems also beendet, und die obigen Fragen können hier leicht beantwortet werden: ReactDer Ereignisname und das Ereignisverarbeitungsfunktionspaar sind gebunden, und 创建rootFiberwenn es fertig ist 事件注册, 事件绑定, 事件调度. Dann ist ihr Ausführungsprozess ungefähr wie folgt:

Zusammenfassen

Dieses Kapitel stellt hauptsächlich die Lebenszyklus-Ausführungssequenz von Komponenten in den Phasen mount, update, und sowie die Registrierung, Bindung, Planungsaktualisierung usw. des Ereignissystems vor.destroyReact

Zu guter Letzt

Organisierte eine Reihe von "Interview-Sammlungen von Front-End-Herstellern", einschließlich HTML, CSS, JavaScript, HTTP, TCP-Protokoll, Browser, VUE, React, Datenstruktur und Algorithmus, insgesamt 201 Interviewfragen, und beantwortete jede Frage Beantworten und analysieren.

Freunde in Not, Sie können auf die Karte am Ende des Artikels klicken, um dieses Dokument zu erhalten und es kostenlos zu teilen

Ein Teil der Dokumentation zeigt:



Die Länge des Artikels ist begrenzt, und die folgenden Inhalte werden nicht einzeln angezeigt

Freunde in Not, Sie können auf die Karte unten klicken, um sie kostenlos zu erhalten

Ich denke du magst

Origin blog.csdn.net/web22050702/article/details/128653976
Empfohlen
Rangfolge