Ein Blick hinter die Kulissen eines modernen Webbrowsers (Teil 4)

Mariko Kosaka

Eingabe kommt an den Compositor

Dies ist der letzte Teil der vierteiligen Blogreihe mit Einblicken in Chrome: der Verarbeitung von Code zum Anzeigen einer Website. Im vorherigen Beitrag haben wir uns mit dem Renderingprozess und dem Compositor vertraut gemacht. In diesem Beitrag schauen wir uns an, wie der Compositor reibungslose Interaktionen bei Nutzereingaben ermöglicht.

Eingabeereignisse aus Browsersicht

Wenn Sie „Ereignisse eingeben“ hören fällt Ihnen wahrscheinlich nur die Eingabe in ein Textfeld oder ein Mausklick ein. aus der Perspektive des Browsers bedeutet, bedeutet die Eingabe jede Geste des Nutzers. Das Scrollen mit dem Mausrad ist eine Eingabe, -Ereignis und Berühren oder Mouseover sind ebenfalls ein Eingabeereignis.

Wenn eine Nutzergeste wie eine Berührung auf einem Bildschirm auftritt, ist der Browserprozess derjenige, der die zu beginnen. Der Browserprozess erkennt jedoch nur, wo die Geste seit Inhalte innerhalb eines Tabs werden vom Renderer-Prozess verarbeitet. Der Browserprozess sendet also das Ereignis -Typ (z. B. touchstart) und seine Koordinaten an den Renderer-Prozess übergeben. Der Renderer-Prozess verarbeitet Ereignis entsprechend kennzeichnen, indem Sie das Ereignisziel suchen und die angehängten Event-Listener ausführen.

<ph type="x-smartling-placeholder">
</ph> Eingabeereignis <ph type="x-smartling-placeholder">
</ph> Abbildung 1: Durch den Browserprozess an den Renderer-Prozess weitergeleitetes Eingabeereignis

Compositor empfängt Eingabeereignisse

<ph type="x-smartling-placeholder">
</ph>
Abbildung 2: Darstellungsbereich mit dem Mauszeiger auf Seitenebenen

Im vorherigen Post haben wir uns damit befasst, wie der Compositor das Scrollen durch gerasterten Ebenen zu erstellen. Wenn der Seite keine Eingabeereignis-Listener angehängt sind, kann der Compositor-Thread Erstellen Sie einen neuen zusammengesetzten Frame, der völlig unabhängig vom Hauptthread ist. Aber was ist, wenn ein Ereignis an die Seite angehängt wurden? Wie würde der Compositor-Thread herausfinden, ob das Ereignis gehandhabt werden?

Informationen zu nicht schnell scrollbarem Bereich

Da das Ausführen von JavaScript der Job des Hauptthreads ist, wird beim Zusammensetzen einer Seite der Compositor-Thread verwendet. markiert einen Bereich der Seite, an den Event-Handler als "Region ohne schnelles Scrollen" angehängt sind. Von Mit diesen Informationen kann der Compositor-Thread sicherstellen, dass das Eingabeereignis an den Hauptthread gesendet wird. wenn das Ereignis in dieser Region auftritt. Wenn das Eingabeereignis von außerhalb dieser Region stammt, ist das Ereignis Der Compositor-Thread setzt den Aufbau eines neuen Frames fort, ohne auf den Hauptthread zu warten.

<ph type="x-smartling-placeholder">
</ph> eingeschränkter Bereich, der nicht schnell scrollbar ist <ph type="x-smartling-placeholder">
</ph> Abbildung 3: Diagramm der beschriebenen Eingabe in den nicht schnellen scrollbaren Bereich

Vorsicht beim Schreiben von Event-Handlern

Ein gängiges Muster bei der Ereignisverarbeitung in der Webentwicklung ist die Ereignisdelegierung. Da Termine in Bubble erscheinen, kann einen Event-Handler an das oberste Element anhängen und Aufgaben basierend auf dem Ereignisziel delegieren. Ich Code wie den folgenden Code geschrieben oder gesehen haben.

document.body.addEventListener('touchstart', event => {
    if (event.target === area) {
        event.preventDefault();
    }
});

Da Sie nur einen Event-Handler für alle Elemente schreiben müssen, ist die Ergonomie dieses Ereignisses Delegationsmuster sind attraktiv. Wenn Sie sich diesen Code jedoch vom Standpunkt des Browsers aus ansehen, angezeigt wird, ist jetzt die gesamte Seite als nicht schnell scrollbarer Bereich markiert. Selbst wenn Ihre Anwendung keine Eingaben aus bestimmten Teilen der Seite interessiert, muss der Compositor-Thread mit dem Hauptthread kommunizieren und bei jedem Eingang eines Eingabeereignisses darauf warten. Das heißt, die flüssiges Scrollen des Compositors wird verhindert.

<ph type="x-smartling-placeholder">
</ph> Ganzseitiger Bereich mit nicht schnell scrollendem Scrollen <ph type="x-smartling-placeholder">
</ph> Abbildung 4: Diagramm der beschriebenen Eingabe für den nicht schnellen scrollbaren Bereich, der eine gesamte Seite abdeckt

Sie können passive: true Optionen in Ihrem Ereignis übergeben, um dieses Problem zu vermeiden Listener. Dies weist den Browser darauf hin, dass Sie das Ereignis im Hauptthread immer noch überwachen möchten. compositor kann aber auch einen neuen Frame zusammensetzen.

document.body.addEventListener('touchstart', event => {
    if (event.target === area) {
        event.preventDefault()
    }
 }, {passive: true});

Prüfen, ob der Termin abgesagt werden kann

<ph type="x-smartling-placeholder">
</ph> Seite scrollen <ph type="x-smartling-placeholder">
</ph> Abbildung 5: Eine Webseite, bei der ein Teil der Seite auf horizontales Scrollen fixiert ist

Angenommen, Sie haben auf einer Seite ein Feld, in dem Sie die Scrollrichtung auf horizontales Scrollen beschränken möchten.

Wenn Sie die Option passive: true in Ihrem Zeigerereignis verwenden, kann das Scrollen der Seite gleichmäßig, kann das vertikale Scrollen um die Zeit begonnen haben, die du preventDefault, um die Begrenzung Scrollrichtung. Sie können dies mithilfe der Methode event.cancelable prüfen.

document.body.addEventListener('pointermove', event => {
    if (event.cancelable) {
        event.preventDefault(); // block the native scroll
        /*
        *  do what you want the application to do here
        */
    }
}, {passive: true});

Alternativ können Sie eine CSS-Regel wie touch-action verwenden, um den Event-Handler vollständig zu entfernen.

#area {
  touch-action: pan-x;
}

Ereignisziel finden

<ph type="x-smartling-placeholder">
</ph> Treffertest <ph type="x-smartling-placeholder">
</ph> Abbildung 6: Hauptthread mit Blick auf die Paint-Datensätze, in denen gefragt wird, was auf dem x.y-Punkt gezeichnet wurde

Wenn der zusammengesetzte Thread ein Eingabeereignis an den Hauptthread sendet, wird als Erstes ein Treffer ausgeführt um das Ereignisziel zu finden. Der Treffertest verwendet Paint-Datensatzdaten, die im Rendering generiert wurden. um herauszufinden, was sich unterhalb der Punktkoordinaten befindet, in denen das Ereignis aufgetreten ist.

Ereignisweiterleitungen an den Hauptthread minimieren

Im vorherigen Beitrag haben wir besprochen, wie ein herkömmliches Display den Bildschirm 60-mal pro Sekunde aktualisiert wie wir mit dem Rhythmus für eine flüssige Animation Schritt halten müssen. Ein typischer Touchscreen zur Eingabe 60- bis 120-mal pro Sekunde ein Touch-Ereignis, eine typische Maus 100-mal pro Sekunde 2. Das Eingabeereignis hat eine höhere Genauigkeit, als unser Bildschirm aktualisiert werden kann.

Wenn ein fortlaufendes Ereignis wie touchmove 120 Mal pro Sekunde an den Hauptthread gesendet wurde, kann eine übermäßige Anzahl von Treffertests und JavaScript-Ausführungen auslösen, verglichen mit der langsamen Ausführung von aktualisiert werden kann.

<ph type="x-smartling-placeholder">
</ph> nicht gefilterte Ereignisse <ph type="x-smartling-placeholder">
</ph> Abbildung 7: Ereignisse, die die Frame-Zeitachse überfluten, die zu Seitenverzögerungen führen

Um übermäßig viele Aufrufe an den Hauptthread zu minimieren, fasst Chrome kontinuierliche Ereignisse zusammen, z. B. wheel, mousewheel, mousemove, pointermove, touchmove) und Verzögerungen beim Versand bis direkt vor dem nächsten requestAnimationFrame ein.

<ph type="x-smartling-placeholder">
</ph> Zusammengeführte Ereignisse <ph type="x-smartling-placeholder">
</ph> Abbildung 8: Gleicher Zeitplan wie zuvor, aber Ereignis wird zusammengeführt und verzögert

Alle diskreten Ereignisse wie keydown, keyup, mouseup, mousedown, touchstart und touchend sofort versandt werden.

getCoalescedEvents für Intra-Frame-Ereignisse verwenden

Bei den meisten Webanwendungen sollten zusammengeführte Ereignisse ausreichen, um eine gute Nutzererfahrung zu bieten. Wenn Sie jedoch z. B. eine Zeichenanwendung erstellen und einen Pfad basierend auf touchmove-Koordinaten können die Koordinaten verloren gehen, um eine gleichmäßige Linie zu zeichnen. In diesem Fall können Sie die Methode getCoalescedEvents im Zeigerereignis verwenden, um Informationen über diese Ereignisse zu erhalten. zusammengeführten Ereignissen.

<ph type="x-smartling-placeholder">
</ph> getCoalescedEvents <ph type="x-smartling-placeholder">
</ph> Abbildung 9: Linker Pfad für sanfte Touch-Gesten, zusammenhängender begrenzter Pfad rechts
window.addEventListener('pointermove', event => {
    const events = event.getCoalescedEvents();
    for (let event of events) {
        const x = event.pageX;
        const y = event.pageY;
        // draw a line using x and y coordinates.
    }
});
.

Nächste Schritte

In dieser Reihe haben wir uns mit der Funktionsweise eines Webbrowsers befasst. Wenn Sie noch nie darüber nachgedacht haben, DevTools empfiehlt, deinem Event-Handler {passive: true} hinzuzufügen oder warum du async schreiben könntest im Skript-Tag einfügen. Ich hoffe, dass Sie in dieser Reihe erklären konnten, um eine schnellere und reibungslosere Webnutzung zu ermöglichen.

Lighthouse verwenden

Wenn Sie Ihren Code für den Browser ansprechend gestalten möchten, aber nicht wissen, wo Sie anfangen sollen, Lighthouse ist ein Tool, mit dem alle Websites geprüft werden können. darüber zu berichten, was richtig gemacht wird und was verbessert werden muss. Liste der Audits durchlesen vermittelt Ihnen auch eine Vorstellung davon, was für einen Browser wichtig ist.

Weitere Informationen zum Messen der Leistung

Die Leistungsoptimierung kann je nach Website variieren. Daher ist es wichtig, dass Sie die Leistung messen. Ihrer Website und entscheiden Sie, was am besten zu Ihrer Website passt. Das Chrome-Entwicklertools-Team bietet einige Tutorials zu Wie Sie die Leistung Ihrer Website messen können.

Funktionsrichtlinie zur Website hinzufügen

Wenn Sie einen zusätzlichen Schritt machen möchten, können Sie die Funktionsrichtlinien Webplattformfunktion, die Sie bei der Erstellung Ihres Projekts Wird aktiviert Funktionsrichtlinien garantiert ein bestimmtes Verhalten Ihrer App und verhindert Fehler. Wenn Sie beispielsweise sicherstellen möchten, dass Ihre Anwendung das Parsing nie blockiert, können Sie Ihre Anwendung auf Richtlinie für synchrone Skripts. Wenn sync-script: 'none' aktiviert ist, wird JavaScript-Code, der den Parser blockiert, ausgeführt werden kann. Dadurch wird verhindert, dass Ihr Code den Parser blockiert und der muss der Parser vom Browser nicht pausiert werden.

Zusammenfassung

Danke

Als ich mit dem Erstellen von Websites anfing, war mir fast nur wichtig, wie ich meinen Code schreibe und was produktiver zu sein. Diese Dinge sind wichtig, aber wir sollten auch berücksichtigen, der von uns geschriebene Code übernimmt. Moderne Browser haben und investieren immer mehr in Möglichkeiten, um die Nutzererfahrung im Web zu verbessern. Um den Browser freundlich zu behandeln, indem wir unseren Code organisieren, verbessert die User Experience. Hoffentlich unterstützen Sie mich bei unserem Bestreben, nett zu den Browsern zu sein!

Ein großes Dankeschön an alle, die erste Entwürfe dieser Serie gelesen haben, einschließlich, aber nicht beschränkt An): Alex Russell, Paul Irish, Meggin Kearney Eric Bidelman Mathias Bynens Addy Osmani, Kinuko Yasuda, Nasko Oskov und Charlie Reis.

Hat Ihnen diese Reihe gefallen? Wenn Sie Fragen oder Vorschläge für den zukünftigen Beitrag haben, Ich würde mich freuen, von dir unten in den Kommentaren zu hören oder @kosamari zu schreiben. auf Twitter.