Wir haben die Delegierung in JavaScript vergessen. Ereignisdelegation reagieren

Hallo an alle. Dieser Artikel befasst sich mit der Ereignisdelegierung in JavaScript und ihrer Implementierung in react.js.



Worum geht es eigentlich? Warum und warum?


Lassen Sie uns zunächst kurz diskutieren:


  1. Was ist ein Ereignis?
  2. wie die Verteilung erfolgt;
  3. Verarbeitung von DOM Level 2 mit einem Beispiel in JavaScript;

Und zum Schluss: Warum nicht die Delegation in React vergessen?


Ereignis


JavaScript und HTML interagieren über Ereignisse miteinander. Jedes Ereignis dient dazu, JavaScript mitzuteilen, dass im Dokument- oder Browserfenster etwas passiert ist. Um diese Ereignisse abzufangen, benötigen wir Listener (Listener), solche Handler, die im Falle eines Ereignisses ausgelöst werden.


Ereignisausbreitung


Bestellung. Lösung des Problems: Wie kann man verstehen, zu welchem ​​Teil der Seite das Ereignis gehört? Es wurden zwei Methoden implementiert: in Internet Explorer "Event Bubbling" und in Netscape Communicator "Event Hooking".


Ereignis sprudelt


In diesem Fall wird das Ereignis am tiefsten Knoten im Dokumentbaum ausgelöst und steigt dann durch die Hierarchie zum Fenster selbst auf.


<!DOCTYPE html> <html> <head> <title>Some title</title> </head> <body> <div id="myDiv">Click Me</div> </body> </html> 

In diesem Fall gibt es die folgende Reihenfolge:


  1. div Element
  2. Körperelement
  3. HTML-Element
  4. Dokument
  5. Fenster

Jetzt wird das Surfen von allen modernen Browsern unterstützt, allerdings mit unterschiedlichen Implementierungen.

Beim Abfangen von Ereignissen ist das Gegenteil der Fall:


  1. Fenster
  2. Dokument
  3. HTML-Element
  4. Körperelement
  5. div Element

Es wurde angenommen, dass das Ereignis verarbeitet werden kann, bevor es das Zielelement erreicht (wie in Netscape entschieden, wurden später alle modernen Browser übernommen).

Als Ergebnis haben wir die folgende Struktur zum Verteilen von DOM-Ereignissen:


  1. Fenster
  2. Dokument
  3. HTML-Element
  4. Körperelement // Die Abfangphase endet
  5. div Element // Zielphase
  6. Körperelement // Die Aufstiegsphase beginnt
  7. HTML-Element
  8. Dokument
  9. Fenster

Dieses Schema ist in drei Phasen unterteilt: die Abfangphase - das Ereignis kann abgefangen werden, bevor es auf das Element trifft, die Zielphase - Verarbeitung durch das Zielelement und die Aufstiegsphase -, um alle endgültigen Aktionen als Reaktion auf das Ereignis auszuführen.


Wir wenden uns also der Ereignisverarbeitung zu


Sehen wir uns ein typisches Beispiel für die Ereignisbehandlung in JavaScript an.


 const btn = document.getElementById('myDiv') btn.addEventListener("click", handler) // some code btn.removeEventListener("click", handler) 

Alles wäre nichts, aber hier erinnern wir uns an unseren geliebten IE, der Ereignisse mit attachEvent abonniert und removeEvent entfernt. Und Sie können die Veranstaltung mehrmals abonnieren. Und vergessen Sie nicht, dass wir durch das Signieren einer anonymen Funktion nicht die Möglichkeit haben, uns abzumelden.


Aber wir sind keine externen Programmierer. Machen wir alles nach dem Kanon:


 var EventUtil = { addHandler: function (elem, type, handler) { if (elem.addEventListener) { elem.addEventListener(type, handler, false) } else if (elem.attachEvent) { elem.attachEvent("on" + type, handler) } else { elem["on" = type] = hendler } }, removeHandler: function (elem, type, handler) { if (elem.removeEventListener) { elem.removeEventListener(type, handler, false) } else if (elem.detachEvent) { elem.detachEvent("on" + type, handler) } else { elem["on" = type] = null } } } 

So gut, aber was ist mit dem Ereignisobjekt? Immerhin gibt es im IE kein .target .srcElement, verhindernDefault? no returnValue = false. Es gibt jedoch nichts, was ein paar Methoden hinzufügen könnte:


 var EventUtil = { addHandler: function (elem, type, handler) { if (elem.addEventListener) { elem.addEventListener(type, handler, false) } else if (elem.attachEvent) { elem.attachEvent("on" + type, handler) } else { elem["on" = type] = hendler } }, getEvent: function (event) { return event ? event : window.event }, getTarget: function (event) { return event.target || event.srcElement }, preventDefault: function (event) { if (event.preventDefault) { event.preventDefault() } else { event.returnValue = false } }, removeHandler: function (elem, type, handler) { if (elem.removeEventListener) { elem.removeEventListener(type, handler, false) } else if (elem.detachEvent) { elem.detachEvent("on" + type, handler) } else { elem["on" = type] = null } }, stopPropagation: function (event) { if (event.stopPropagation) { event.stopPropagation() } else { event.cancelBubble = true } } } 

Usw. usw. und das sind alles Tänze.


Nun, wir sind gut gemacht, wir haben alle Probleme gelöst, alles ist in Ordnung. Der Code war zwar ziemlich umständlich. Stellen Sie sich nun vor, wir brauchen viele Abonnements für viele Elemente. Wow, das wird einige Codezeilen dauern. Ein Beispiel:


 <ul> <li id="id1">go somewhere</li> <li id="id2">do something</li> <li id="some-next-id">next</li> </ul> var item1 = document.getElementById('id1') var item2 = document.getElementById('id2') var itemNext = document.getElementById('some-next-id') EventUtil.addHandler(item1, "click", someHandle) EventUtil.addHandler(item2, "click", someHandle2) EventUtil.addHandler(itemNext, "click", someHandle3) 

Und so darf für jedes Element, und wir dürfen nicht vergessen zu löschen, mit Ziel und dergleichen arbeiten


Und hier hilft uns die Eventdelegation.


Wir müssen lediglich einen einzelnen Handler mit dem höchsten Punkt im DOM-Baum verbinden:


 <ul id="main-id"> //  id    <li id="id1">go somewhere</li> <li id="id2">do something</li> <li id="some-next-id">next</li> </ul> var list = document.getElementById('main-id') EventUtil.addHandler(list, "click", function(event) { event = EventUtil.getEvent(event) var target = EventUtil.getTarget(event) switch(target.id) { case "id1": //  -    id1 break case "id2": //  -    id1 break case "some-next-id": //  -    break } }) 

Infolgedessen haben wir nur einen Handler im Speicher, und die Eigenschaft id kann für die gewünschte Aktion verwendet werden. Ein geringerer Speicherverbrauch verbessert die Gesamtseitenleistung. Das Registrieren eines Ereignishandlers erfordert weniger Zeit und weniger Aufrufe an das DOM. Die Ausnahme ist vielleicht Mouseover und Mouseout, bei ihnen ist alles etwas komplizierter.


Was ist nun mit Reagieren?


Alles, was die Cross-Browser-Kompatibilität für uns betrifft, wurde bereits von Leuten von Facebook erledigt. Alle unsere Event-Handler erhalten eine Instanz von SyntheticEvent . Dadurch werden Ereignisse aus dem Pool wiederverwendet und alle Eigenschaften nach dem Aufruf des Handlers entfernt.


Gut.


Ein zusätzlicher Handler ist jedoch ein zusätzlicher Handler. Ich habe mich mehrmals getroffen und bereue mich selbst. Ich habe diese Art von Code geschrieben:


 class Example extends React.Component { handleClick () { console.log('click') } render () { return ( <div> {new Array(20).fill().map((_, index) => <div key={index} // elem.id id={index} // elem.id onClick={() => console.log('click')} /> )} </div> ) } } 

Das Beispiel zeigt den Fall, wenn es eine Art Blatt mit n-Anzahl von Elementen und daher mit n-Anzahl von Registrierungen von Handlern gibt.


Lassen Sie uns loslegen, zur Seite gehen und überprüfen, wie viele Handler gerade in Aktion sind. Dafür habe ich ein gutes Skript gefunden:


 Array.from(document.querySelectorAll('*')) .reduce(function(pre, dom){ var clks = getEventListeners(dom).click; pre += clks ? clks.length || 0 : 0; return pre }, 0) 

Funktioniert in Chrome Dev-Tool.


Und jetzt delegieren wir all dies an das übergeordnete div und Prost, wir haben gerade unsere Anwendung n = array.length times optimiert. Beispielcode unten:


 class Example extends React.Component { constructor () { super() this.state = { useElem: 0 } } handleClick (elem) { var id = elem.target.id this.setState({ useElem: id }) } render () { return ( <div onClick={this.handleClick}> {new Array(20).fill().map((_, index) => <div key={index} // elem.id id={index} // elem.id useElem={index === this.state.useElem} /> )} </div> ) } } 

Die Delegierung ist ein gutes Werkzeug für die Verarbeitung einer großen Anzahl von Abonnements. Bei dynamischem Rendern und häufigen Neuzeichnungen ist sie einfach unersetzlich. Schade um die Ressourcen des Benutzers, sie sind nicht unbegrenzt.


Dieser Artikel basiert auf einem JavaScript-Buch für professionelle Webentwickler, das von Nicholas Zakas geschrieben wurde.


Vielen Dank für Ihre Aufmerksamkeit. Wenn Sie etwas zu teilen haben oder einen Fehler finden, vielleicht einen Fehler oder nur eine Frage, dann schreiben Sie in die Kommentare. Ich freue mich über jedes Feedback!

Source: https://habr.com/ru/post/de467361/


All Articles