Der Ereignismechanismus in React v17 hat große Änderungen erfahren und unterscheidet sich meiner Meinung nach deutlich von v16. Die in diesem Artikel analysierte React-Version ist 17.0.1 und die Anwendung wird mit ReactDOM.render erstellt, ohne prioritätsbezogene Informationen. PrinzipEreignisse in React werden in delegierte Ereignisse (DelegatedEvent) und nicht delegierte Ereignisse (NonDelegatedEvent) unterteilt. Wenn ein delegiertes Ereignis von fiberRoot erstellt wird, bindet es fast alle Ereignisverarbeitungsfunktionen an das DOM-Element des Stammknotens, während nicht delegierte Ereignisse die Verarbeitungsfunktionen nur an das DOM-Element selbst binden. Gleichzeitig unterteilt React Ereignisse in drei Typen: diskretesEvent, userBlockingEvent und kontinuierlichesEvent. Sie haben unterschiedliche Prioritäten und verwenden beim Binden von Ereignisverarbeitungsfunktionen unterschiedliche Rückruffunktionen. React-Ereignisse werden auf nativer Basis erstellt und simulieren eine Reihe von Mechanismen zum Aufsteigen und Erfassen von Ereignissen. Wenn ein DOM-Element ein Ereignis auslöst, sprudelt es zur Verarbeitungsfunktion, die an den Stammknoten von React gebunden ist, und erhält das DOM-Objekt, das das Ereignis ausgelöst hat, und den entsprechenden Fiber-Knoten über das Ziel. Der Fiber-Knoten durchläuft das übergeordnete übergeordnete Element, sammelt eine Ereigniswarteschlange und durchläuft dann die Warteschlange, um die Ereignisverarbeitungsfunktion auszulösen, die jedem Fiber-Objekt in der Warteschlange entspricht. Die Vorwärtsdurchquerung simuliert das Aufsteigen und die Rückwärtsdurchquerung simuliert das Erfassen, sodass das synthetische Ereignis nach dem nativen Ereignis ausgelöst wird. Die dem Fiber-Objekt entsprechende Ereignisbehandlungsfunktion ist weiterhin in den Props gespeichert. Die Sammlung wird einfach aus den Props entnommen und ist an kein Element gebunden. QuellcodeanalyseDer folgende Quellcode stellt lediglich eine kurze Analyse der grundlegenden Logik dar. Ziel ist es, den Auslösevorgang des Ereignismechanismus zu verdeutlichen und viel prozessirrelevanten oder komplexen Code zu entfernen. Delegierte EreignisbindungDieser Schritt erfolgt, wenn ReactDOM.render aufgerufen wird. Wenn fiberRoot erstellt wird, werden alle unterstützten Ereignisse auf dem DOM-Element des Stammknotens abgehört. Funktion createRootImpl( Container: Container, tag: RootTag, Optionen: void | RootOptions, ) { // ... const rootContainerElement = container.nodeType === COMMENT_NODE ? container.parentNode : Container; // Auf alle unterstützten Ereignisse achten listenToAllSupportedEvents(rootContainerElement); // ... } listenToAllSupportedEventsBeim Binden eines Ereignisses wird der entsprechende Ereignisname über eine Set-Variable namens allNativeEvents abgerufen. Diese Variable wird in einer Funktion der obersten Ebene gesammelt, und nonDelegatedEvents ist ein vordefiniertes Set. Exportfunktion listenToAllSupportedEvents(rootContainerElement: EventTarget) { allNativeEvents.fürEach(domEventName => { // Ereignisse ausschließen, die keine Delegierung erfordern, wenn (!nonDelegatedEvents.has(domEventName)) { // Blase listenToNativeEvent( domEreignisname, FALSCH, ((rootContainerElement: beliebig): Element), null, ); } // Erfassen Sie „listenToNativeEvent“ ( domEreignisname, WAHR, ((rootContainerElement: beliebig): Element), null, ); }); } listenToNativeEventDie Funktion listenToNativeEvent markiert den Ereignisnamen im DOM-Element, bevor das Ereignis gebunden wird, und bindet es nur, wenn es als falsch beurteilt wird. Exportfunktion listenToNativeEvent( domEventName: DOMEventName, isCapturePhaseListener: boolesch, rootContainerElement: EventTarget, targetElement: Element | null, eventSystemFlags?: EventSystemFlags = 0, ): Leere { let Ziel = rootContainerElement; // ... // Speichern Sie ein Set im DOM-Element, um die Ereignisse zu identifizieren, auf die das aktuelle Element wartet. const listenerSet = getEventListenerSet(target); // Ereignisidentifikationsschlüssel, Verarbeitung der Zeichenfolgenverkettung const listenerSetKey = getListenerSetKey( domEreignisname, istCapturePhaseListener, ); wenn (!listenerSet.has(listenerSetKey)) { // Als erfasst markieren, wenn (isCapturePhaseListener) { eventSystemFlags |= IS_CAPTURE_PHASE; } // Bind-Ereignis addTrappedEventListener( Ziel, domEreignisname, Ereignissystemflaggen, istCapturePhaseListener, ); // Zum Set hinzufügen listenerSet.add(listenerSetKey); } } addTrappedEventListenerDie Funktion addTrappedEventListener erhält über den Ereignisnamen die Listener-Funktion mit der entsprechenden Priorität und übergibt sie dann an die Funktion auf niedrigerer Ebene, um die Ereignisbindung zu handhaben. Diese Listener-Funktion ist eine Closure-Funktion, die auf die drei Variablen targetContainer, domEventName und eventSystemFlags zugreifen kann. Funktion addTrappedEventListener( ZielContainer: EventTarget, domEventName: DOMEventName, eventSystemFlags: EventSystemFlags, isCapturePhaseListener: boolesch, isDeferredListenerForLegacyFBSupport?: boolesch, ) { // Holen Sie sich den entsprechenden Listener entsprechend der Priorität let listener = createEventListenerWrapperWithPriority( ZielContainer, domEreignisname, Ereignissystemflaggen, ); wenn (isCapturePhaseListener) { addEventCaptureListener(Zielcontainer, DomEventName, Listener); } anders { addEventBubbleListener(Zielcontainer, DomEventName, Listener); } } Die Funktionen addEventCaptureListener und addEventBubbleListener rufen den nativen target.addEventListener auf, um Ereignisse zu binden. Dieser Schritt besteht darin, einen Satz mit Ereignisnamen zu durchlaufen und die dem jeweiligen Ereignis entsprechende Verarbeitungsfunktion an das DOM-Element des Stammknotens zu binden. Keine Delegatenereignisbindung erforderlichZu den Veranstaltungen ohne Delegationspflicht zählen auch Veranstaltungen mit Medienelementen. export const nonDelegatedEvents: Set<DOMEventName> = neues Set([ 'stornieren', 'schließen', 'ungültig', 'laden', 'scrollen', 'Umschalten', ...Medienereignistypen, ]); exportiere const mediaEventTypes: Array<DOMEventName> = [ 'abbrechen', 'kann spielen', 'kanndurchspielen', 'Daueränderung', 'geleert', 'verschlüsselt', 'beendet', 'Fehler', 'geladene Daten', „geladene Metadaten“, 'Ladestart', 'Pause', 'spielen', 'spielen', 'Fortschritt', 'Ratenänderung', 'gesucht', 'suchend', 'in die Blockade geraten', 'aussetzen', 'Zeitaktualisierung', 'Lautstärkeänderung', 'Warten', ]; Festlegen anfänglicher EigenschaftenDie Methode setInitialProperties wird ohne Delegation direkt an das DOM-Element selbst gebunden und legt auch den Stil und einige eingehende DOM-Attribute fest. Exportfunktion setInitialProperties( domElement: Element, Tag: Zeichenfolge, rawProps: Objekt, rootContainerElement: Element | Dokument, ): Leere { let-Requisiten: Objekt; Schalter (Tag) { // ... Fall 'Video': Fall 'Audio': für (let i = 0; i < mediaEventTypes.length; i++) { listenToNonDelegatedEvent(mediaEventTypes[i], domElement); } Requisiten = Rohprops; brechen; Standard: Requisiten = Rohprops; } // DOM-Attribute festlegen, etwa Stil … setInitialDOMProperties( Etikett, domElement, rootContainerElement, Requisiten, istCustomComponentTag, ); } Im Switch werden die entsprechenden Ereignisse entsprechend den verschiedenen Elementtypen gebunden. Hier bleibt nur die Verarbeitung von Videoelementen und Audioelementen übrig. Sie durchlaufen mediaEventTypes, um die Ereignisse an die DOM-Elemente selbst zu binden. ListenToNonDelegatedEvent (Nicht delegiertes Ereignis)Die Logik der Methode listenToNonDelegatedEvent ist grundsätzlich dieselbe wie die der Methode listenToNativeEvent im vorherigen Abschnitt. Exportfunktion listenToNonDelegatedEvent( domEventName: DOMEventName, Zielelement: Element, ): Leere { const isCapturePhaseListener = false; const listenerSet = getEventListenerSet(targetElement); const listenerSetKey = getListenerSetKey( domEreignisname, istCapturePhaseListener, ); wenn (!listenerSet.has(listenerSetKey)) { addTrappedEventListener( Zielelement, domEreignisname, IS_NON_DELEGATED, istCapturePhaseListener, ); listenerSet.add(listenerSetKey); } } Es ist erwähnenswert, dass, obwohl die Ereignisverarbeitung an das DOM-Element selbst gebunden ist, die gebundene Ereignisverarbeitungsfunktion nicht die im Code übergebene Funktion ist und nachfolgende Trigger die Verarbeitungsfunktion weiterhin zur Ausführung abrufen. EreignishandlerDie Ereignisbehandlungsfunktion bezieht sich auf die Standardbehandlungsfunktion in React, nicht auf die im Code übergebene Funktion. Diese Funktion wird von der Methode createEventListenerWrapperWithPriority erstellt und die entsprechenden Schritte finden Sie oben im addTrappedEventListener. Erstellen Sie einen EventListenerWrapper mit Priorität.Exportfunktion createEventListenerWrapperWithPriority( ZielContainer: EventTarget, domEventName: DOMEventName, eventSystemFlags: EventSystemFlags, ): Funktion { // Ereignispriorität aus der integrierten Map abrufen const eventPriority = getEventPriorityForPluginSystem(domEventName); lass listenerWrapper; //Gibt unterschiedliche Listener entsprechend unterschiedlicher Prioritäten zurück Schalter (Ereignispriorität) { Fall DiscreteEvent: listenerWrapper = DispatchDiskretesEreignis; brechen; Fall UserBlockingEvent: listenerWrapper = dispatchUserBlockingUpdate; brechen; Fall ContinuousEvent: Standard: listenerWrapper = DispatchEvent; brechen; } returniere listenerWrapper.bind( null, domEreignisname, Ereignissystemflaggen, ZielContainer, ); } Die Funktion createEventListenerWrapperWithPriority gibt den Listener zurück, der der Ereignispriorität entspricht. Diese drei Funktionen erhalten alle vier Parameter. Funktion fn( domEreignisname, Ereignissystemflaggen, Container, nativesEreignis, ) { //... } Bei der Rückgabe wird Bind in 3 Parametern übergeben, sodass die zurückgegebene Funktion eine Verarbeitungsfunktion ist, die nur nativeEvent empfängt, aber auf die ersten 3 Parameter zugreifen kann. Die Methode dispatchEvent wird tatsächlich intern von der Methode dispatchDiscreteEvent und der Methode dispatchUserBlockingUpdate aufgerufen. VersandereignisHier wurde viel Code entfernt, angezeigt wird lediglich der Code, der das Ereignis auslöst. Exportfunktion dispatchEvent( domEventName: DOMEventName, eventSystemFlags: EventSystemFlags, ZielContainer: EventTarget, nativeEvent: Jedes nativeEvent, ): Leere { // ... // Ereignis „tryToDispatchEvent“ auslösen ( domEreignisname, Ereignissystemflaggen, ZielContainer, nativesEreignis, ); // ... } Die Methode „temptToDispatchEvent“ verarbeitet immer noch eine Menge komplexer Logik und es gibt mehrere Ebenen des Funktionsaufrufstapels. Wir werden sie alle überspringen und uns nur die Tastentriggerfunktion ansehen. dispatchEventsForPluginsDie Funktion dispatchEventsForPlugins sammelt die Verarbeitungsfunktionen, die den Knoten auf jeder Ebene entsprechen, die das Ereignis auslösen, d. h. die Funktionen, die wir tatsächlich an JSX übergeben, und führt sie aus. Funktion dispatchEventsForPlugins( domEventName: DOMEventName, eventSystemFlags: EventSystemFlags, nativeEvent: Jedes nativeEvent, targetInst: null | Faser, ZielContainer: EventTarget, ): Leere { const nativeEventTarget = getEventTarget(nativeEvent); const dispatchQueue: DispatchQueue = []; //Sammle Listener, um sprudelnde extractEvents zu simulieren( Versandwarteschlange, domEreignisname, ZielInst, nativesEreignis, nativesEventTarget, Ereignissystemflaggen, ZielContainer, ); //Ausführungswarteschlange processDispatchQueue(dispatchQueue, eventSystemFlags); } Ereignisse extrahierenDie Funktion „extractEvents“ erstellt hauptsächlich entsprechende synthetische Ereignisse für verschiedene Ereignistypen und sammelt die Listener der Knoten auf jeder Ebene, um das Sprudeln oder Erfassen zu simulieren. Der Code hier ist länger und viel irrelevanter Code wurde gelöscht. Funktion extractEvents( dispatchQueue: Versandwarteschlange, domEventName: DOMEventName, targetInst: null | Faser, nativeEvent: Jedes nativeEvent, nativeEventTarget: null | EventTarget, eventSystemFlags: EventSystemFlags, ZielContainer: EventTarget, ): Leere { const reactName = topLevelEventsToReactNames.get(domEventName); lassen Sie SyntheticEventCtor = SyntheticEvent; Lassen Sie reactEventType: Zeichenfolge = domEventName; //Erstelle unterschiedliche synthetische Ereignisse entsprechend den unterschiedlichen Ereignissen switch (domEventName) { Fall 'Tastendruck': Fall 'Taste gedrückt': Fall 'keyup': SyntheticEventCtor = SynthetischesTastaturereignis; brechen; Fall 'Klick': // ... Fall 'mouseover': SyntheticEventCtor = SynthetischesMausereignis; brechen; Fall 'ziehen': // ... Fall 'drop': SyntheticEventCtor = SynthetischesDragEvent; brechen; // ... Standard: brechen; } // ... // Sammeln Sie Zuhörer auf jeder Ebene const listeners = acquireSinglePhaseListeners( ZielInst, reagierenName, nativeEvent.Typ, inCapturePhase, NurZielakkumulieren, ); wenn (Listeners.Länge > 0) { // Ein synthetisches Ereignis erstellen const event = new SyntheticEventCtor( Reaktionsname, reagierenEventType, null, nativesEreignis, nativesEventTarget, ); dispatchQueue.push({Ereignis, Zuhörer}); } } Einzelphasenlistener ansammelnDie Funktion „accumulateSinglePhaseListeners“ durchläuft die obere Ebene, um eine Liste zu sammeln, die später zum Simulieren des Sprudelns verwendet wird. Exportfunktion acquireSinglePhaseListeners( targetFiber: Faser | null, reactName: Zeichenfolge | null, nativeEventType: Zeichenfolge, inCapturePhase: Boolesch, acquireTargetOnly: Boolesch, ): Array<DispatchListener> { const captureName = reactName !== null ? reactName + 'Capture' : null; const reactEventName = inCapturePhase? captureName: reactName; const listeners: Array<DispatchListener> = []; lass Instanz = Zielfaser; let lastHostComponent = null; // Durchlaufe den Fiber-Knoten, der das Ereignis auslöst, um DOM und Listener zu sammeln während (Instanz !== null) { const {stateNode, tag} = Instanz; // Nur HostComponents haben Listener (also <div>) wenn (tag === HostComponent && stateNode !== null) { letzteHostkomponente = Statusknoten; wenn (reactEventName !== null) { // Holen Sie die eingehende Ereignis-Listener-Funktion von den Props auf dem Fiber-Knoten const listener = getListener(instance, reactEventName); wenn (listener != null) { listeners.push({ Beispiel, Hörer, aktuellesZiel: letzteHostkomponente, }); } } } wenn (NurZiel akkumulieren) { brechen; } // Weiter nach obeninstance = instance.return; } Zuhörer zurückgeben; } Die endgültige Datenstruktur sieht wie folgt aus: Die Datenstruktur der dispatchQueue ist ein Array vom Typ [{ event,listeners }]. Bei den Listenern handelt es sich um schichtweise gesammelte Daten vom Typ [{ currentTarget, instance, listener }] ProzessDispatchQueueDie DispatchQueue wird in der Funktion processDispatchQueue durchlaufen. Exportfunktion processDispatchQueue( dispatchQueue: Versandwarteschlange, eventSystemFlags: EventSystemFlags, ): Leere { const inCapturePhase = (eventSystemFlags & IS_CAPTURE_PHASE) !== 0; für (lass i = 0; i < dispatchQueue.length; i++) { const {Ereignis, Listener} = dispatchQueue[i]; processDispatchQueueItemsInOrder(Ereignis, Listener, inCapturePhase); } } Jedes Element in der DispatchQueue wird durchlaufen und in der Funktion processDispatchQueueItemsInOrder ausgeführt. processDispatchQueueItemsInOrderFunktion processDispatchQueueItemsInOrder( Ereignis: ReactSyntheticEvent, dispatchListeners: Array<DispatchListener>, inCapturePhase: Boolesch, ): Leere { lass vorherige Instanz; // Erfassen, wenn (inCapturePhase) { für (let i = dispatchListeners.length - 1; i >= 0; i--) { const {Instanz, aktuelles Ziel, Listener} = dispatchListeners[i]; wenn (Instanz !== vorherigeInstanz und Ereignis.isPropagationStopped()) { zurückkehren; } executeDispatch(Ereignis, Listener, aktuellesZiel); vorherigeInstanz = Instanz; } } anders { // Blase für (let i = 0; i < dispatchListeners.length; i++) { const {Instanz, aktuelles Ziel, Listener} = dispatchListeners[i]; wenn (Instanz !== vorherigeInstanz und Ereignis.isPropagationStopped()) { zurückkehren; } executeDispatch(Ereignis, Listener, aktuellesZiel); vorherigeInstanz = Instanz; } } } Die Funktion processDispatchQueueItemsInOrder simuliert das Sprudeln und Erfassen, indem sie je nach Beurteilung vorwärts und rückwärts durchläuft. AusführenDispatchDer Listener wird in der Funktion „executeDispatch“ ausgeführt. Funktion ExecuteDispatch ( Ereignis: ReactSyntheticEvent, Listener: Funktion, aktuellesZiel: Ereignisziel, ): Leere { const Typ = Ereignistyp || 'unbekanntes Ereignis'; event.currentTarget = aktuellesZiel; Listener (Ereignis); Ereignis.aktuellesZiel = null; } AbschlussDieser Artikel soll die Ausführung des Ereignismechanismus erläutern. Er listet einfach die Codelogik entsprechend dem Funktionsausführungsstapel auf. Ohne Vergleich des Codes ist es schwierig, dies zu verstehen. Das Prinzip wird am Anfang erklärt. Der Ereignismechanismus von React ist undurchsichtig und komplex. Er trifft viele Entscheidungen basierend auf unterschiedlichen Situationen und es gibt auch prioritätsbezogene Codes und synthetische Ereignisse. Ich habe sie hier nicht einzeln erklärt. Der Grund dafür ist natürlich, dass ich sie noch nicht gelesen habe. Normalerweise verwende ich React, um einfache mobile Seiten zu schreiben. Mein Chef beschwerte sich immer, dass die Ladegeschwindigkeit nicht schnell genug sei, aber ich konnte nichts dagegen tun. Für meine Arbeit spielte es keine Rolle, ob Cocurrent vorhanden war oder nicht. Die synthetischen Ereignisse waren komplizierter und völlig unnötig. Die Autoren von React waren jedoch sehr kreativ. Wenn ich den Quellcode nicht gelesen hätte, wäre ich nie auf die Idee gekommen, dass sie eine Reihe von Ereignismechanismen simuliert hatten. Kleine Gedanken
Über diese Fragen hatte ich noch nie nachgedacht, aber heute, nachdem ich den Quellcode gelesen hatte, fiel es mir ein.
<div native onClick={(e)=>{e.stopPropagation()}}> <div onClick={()=>{console.log("synthetisches Ereignis")}}>synthetisches Ereignis</div> </div> Beispielsweise gibt die Konsole in diesem Beispiel nicht einmal die vier Wörter „synthetisches Ereignis“ aus, nachdem das native onClick die Übertragung blockiert hat. Oben finden Sie den detaillierten Inhalt der Quellcodeanalyse des React-Ereignismechanismus. Weitere Informationen zum Quellcode des React-Ereignismechanismus finden Sie in den anderen verwandten Artikeln auf 123WORDPRESS.COM! Das könnte Sie auch interessieren:
|
<<: Linux-Tutorial zum Ersetzen von Zeichenfolgen mit dem Befehl sed
>>: Beispiel zur Identifizierung des Benutzers mithilfe eines Linux-Bash-Skripts
6 Lösungen für Netzwerkfehler im Docker-Container...
Nachdem der Server, auf dem sich Docker befindet,...
Erstellen Sie zunächst ein spezielles Projektverz...
Suchen Sie immer noch nach einer Möglichkeit, Hyp...
Zunächst stellen wir vor, wie (1) MySQL 5.7 hat e...
Inhaltsverzeichnis 1. Grundlegende Verwendung von...
Hinweis: Beim Schreiben der Datei docker-compose....
Inhaltsverzeichnis Funktionseinführung Rendern 1....
Inhaltsverzeichnis Vorwort Was macht Yarn Create?...
Inhaltsverzeichnis 1. Reagieren.Children.map 2. R...
Inhaltsverzeichnis 1 Eine kurze Einführung in den...
Inhaltsverzeichnis Vorwort Architektur auf einen ...
stat-Funktion und stat-Befehl Erklärung von [inod...
Heute werden wir ein Thunder Fighter-Tippspiel im...
Jetzt unterstützt der 2016-Server den Multi-Site-...