Das Geheimnis der Ereignisschleife aufdecken

Autor | Xiaoxuan

Einführung 

In diesem Artikel wird der Ereignisschleifenmechanismus umfassend erläutert. In diesem Artikel erfahren Sie mehr über die Beziehung zwischen „Ereignisschleife“ und „Browser-Rendering“, Browser-SetTimeout, RequestAnimationFrame (RAF), RequestIdleCallback (RIC) und anderen APIs in Ereignisse Der „Ausführungszeitpunkt“ des Zyklus, was verursacht, dass der Browser einfriert, wie Interaktionsindikatoren gemessen werden und wie die interaktive Leistung der Website verbessert werden kann.

Der Volltext umfasst 10.503 Wörter und die geschätzte Lesezeit beträgt 27 Minuten.

01 Vorwort

Wir erwähnen oft die Seitenleistung, warum wir lange Aufgaben optimieren müssen und warum React eine Zeitaufteilung erfordert. Dieser Artikel verbindet Browser-Rendering, Ereignisschleife und Seitenleistung.

In diesem Artikel erfahren Sie mehr über die Beziehung zwischen „Ereignisschleife“ und „Browser-Rendering“, Browser-SetTimeout,

Der „Ausführungszeitpunkt“ von APIs wie requestAnimationFrame (RAF) und requestIdleCallback (RIC) in der Ereignisschleife, was das Einfrieren des Browsers verursacht, wie Interaktionsindikatoren gemessen werden und wie die interaktive Leistung der Website verbessert werden kann.

Nachdem Sie dies gelernt haben, können Sie sich mit Fragen wie der Frage befassen, warum RAF für Animationen verwendet werden sollte, wann RIC verwendet werden sollte, ob setTimeout ausgewählt werden sollte und wie lange Aufgaben vermieden werden können.

02 Übersicht über die Ereignisschleife

2.1 Warum sollten wir die Ereignisschleife verstehen?

Ein tiefes Verständnis der Ereignisschleife ist für die Leistungsoptimierung von grundlegender Bedeutung. Bevor wir die Ereignisschleife diskutieren, müssen wir die Multiprozess- und Multithread-Architektur des Browsers verstehen.

2.2 Browser-Architektur

Rückblickend auf die Browserarchitektur sind moderne Browser Multiprozess- und Multithread-Browser.

2.2.1 Multiprozess

Chrome verwendet eine Multiprozessarchitektur, was bedeutet, dass jeder Tab (und in einigen Browsern auch jede Erweiterung) normalerweise in einem eigenen Prozess ausgeführt wird. Dies hat den Vorteil, dass der Absturz eines Tabs keine Auswirkungen auf andere Tabs hat.

Mit der Site-Isolationsfunktion stellt jede Registerkarte des Browsers einen unabhängigen Rendering-Prozess dar. Der Vorteil besteht darin, dass beim Öffnen von drei Registerkarten eine hängengebliebene Registerkarte keine Auswirkungen auf die beiden anderen hat. Wenn jedoch drei Registerkarten denselben Prozess ausführen und einer von ihnen hängen bleibt, bleiben alle hängen, was das Erlebnis sehr schlecht macht.

Bild

△Browser-Multiprozessdiagramm

2.2.2 Multithreading

Jeder Browserprozess kann mehrere Threads enthalten. Beispielsweise wird der Hauptthread zum Ausführen von JavaScript-Code und zum Verwalten des Seitenlayouts verwendet, während andere Threads für Netzwerkanfragen, Rendering und andere Aufgaben verwendet werden können.

Haupt-Bedroung

Webanwendungen müssen bestimmte kritische Vorgänge in diesem einzelnen Hauptthread ausführen. Wenn Sie zu einer Webanwendung navigieren, erstellt der Browser diesen Thread und gewährt ihn Ihrer Anwendung, damit Ihr Code darauf ausgeführt werden kann.

Der Hauptthread bezieht sich auf den Hauptthread im Renderprozess, der für das Parsen von HTML, das Berechnen von CSS-Stilen, das Ausführen von JavaScript, das Berechnen des Layouts, das Zeichnen von Ebenen und andere Aufgaben verantwortlich ist.

Bild

△Der Hauptprozess ist das im Renderprozess enthaltene Thread-Diagramm

Einige Aufgaben müssen im Hauptthread ausgeführt werden. Beispielsweise muss jeder Vorgang, der direkt Zugriff auf das DOM (d. h. das DOM-Dokument) erfordert, im Hauptthread ausgeführt werden (da das DOM nicht threadsicher ist). Dies umfasst den größten Teil des UI-bezogenen Codes.

Der Hauptthread kann jeweils nur eine Aufgabe ausführen .

Darüber hinaus muss eine Aufgabe im Hauptthread vollständig ausgeführt werden, bevor eine andere Aufgabe ausgeführt werden kann. Der Browser verfügt über keinen Mechanismus zur „teilweisen“ Ausführung von Aufgaben; jede Aufgabe wird vollständig ausgeführt, bis sie abgeschlossen ist.

Wenn im folgenden Beispiel der Browser die Benutzeroberfläche anzeigt, werden die folgenden Aufgaben nacheinander ausgeführt und jede Aufgabe wird im Hauptthread abgeschlossen:

Bild

03 Der spezifische Prozess der Ereignisschleife

Was wir hier hauptsächlich besprechen, ist die Fensterereignisschleife. Das heißt, die Ereignisschleife wird vom Hauptthread in einem Rendering-Prozess des Browsers gesteuert.

Bild

△Der spezifische Prozess, bei dem eine Ereignisschleife auftritt

Es tritt eine Ereignisschleife auf, d. h. der Prozess, mit dem der Browser JS in einem Frame ausführen kann, ist wie folgt:

Nehmen Sie eine Aufgabe (Makroaufgabe) aus der Aufgabenwarteschlange, führen Sie sie aus und löschen Sie sie -> Führen Sie alle Jobs (Mikroaufgaben) in der Warteschlange aus und löschen Sie sie -> requestAnimationFrame – Browser-Update-Rendering – requestIdleCallback

3.1 Schritte zum Aktualisieren des Renderings

Die ersten beiden Schritte sind bekannt und werden hier nicht besprochen. Wir konzentrieren uns auf die Schritte nach „Update Rendering“.

1. Rendering-Möglichkeiten: Markieren Sie, ob das Rendering nach einer Ereignisschleife erfolgt. Am Ende jeder Ereignisschleife erfolgt nicht unbedingt ein Rendering. Mögliche Ursachen für das Nicht-Rendering: Die aktuelle Aktualisierungsrate kann nicht beibehalten werden, der Browserkontext ist nicht sichtbar, der Browser stellt fest, dass das Update keine visuellen Änderungen verursacht und der Raf-Callback ist leer.

Wenn keine dieser Bedingungen erfüllt ist und das aktuelle Dokument nicht leer ist, setzen Sie hasARenderingOpportunity auf true.

2. Wenn sich das Fenster ändert, führen Sie eine Größenänderung durch.

3. Wenn Sie scrollen, führen Sie scroll aus.

4. Medienanfragen.

5.Leinwand 。

6. Führen Sie den RAF-Rückruf aus, übergeben Sie den Rückrufparameter DOMHighResTimeStamp und beginnen Sie mit der Ausführung der Rückrufzeit.

7. Führen Sie Berechnungen wie das Layout erneut aus und rendern Sie die Zeichenoberfläche.

8. Wenn sowohl die Aufgabenwarteschlange als auch die Mikrotaskwarteschlange leer sind und der Rendering-Zeitpunkt hasARenderingOpportunity falsch ist, wird geprüft, ob der Ausführungsalgorithmus die Rückruffunktion von requestIdleCallback ausführt.

3.2 Ausführungssequenz und Rendering

Nehmen wir eine einfache Frage: Definieren Sie die Codes zum gleichzeitigen Erstellen von Makroaufgaben, Mikroaufgaben, RIC und RAF und geben Sie die Ausführungssequenz aus.

console.log('开始执行');
console.log('start');
setTimeout(() => {
  console.log('setTimeout');
}, 0);

requestAnimationFrame(() => {
  console.log('requestAnimationFrame');
});
new Promise((resolve, reject) => {
  console.log('Promise');
  resolve('promise resolved');
})

requestIdleCallback(() => {
  console.log('requestIdleCallback');
});

(async function asyncFunction() {
  console.log(await 'asyncFunction');
})();

console.log('执行结束');
// 开始执行
// Promise
// 执行结束
// promise resolved
// asyncFunction
// setTimeout
// requestAnimationFrame
// requestIdleCallback

Sie fragen sich vielleicht, warum RAF vor setTimeout(fn, 0) ausgeführt wird. Der Ausführungszeitpunkt von setTimeout(fn, 0) ist um 0-4 ms verzögert. RAF kann grob verstanden werden als settimeout(fn, Math.random() * 16.6) Daher hat setTimeout Vorrang. Wenn der Hauptthread jedoch vor der Ausführung von setTimeout mit anderen Aufgaben gefüllt ist und die Zeit eines Frames überschreitet, wird setTimeout nach dem RAF-Rückruf ausgeführt (siehe Codeausschnitt unten für Anwendungsfälle), sodass die Verzögerungszeit von setTimeout nicht gilt stabil. Das Ausführungs-Timing von RAF ist stabil. Alles, was in einem Frame registriert wird, wird am Ende dieses Frames und vor dem Start des nächsten Frames ausgeführt.

  let task = new Array(10000).fill(null).map((_, i) => () => {
    const span = document.createElement("span");
    span.innerText = i;
    console.log("==>task", i);
  });
  task.forEach((i) => i());
  requestAnimationFrame(() => {
    console.log("===>requestAnimationFrame");
  });
  setTimeout(() => {
    console.log("===>setTimeout");
  }, 0);
  //输出:
  // ===>requestAnimationFrame
  // ===>setTimeout

Beachten Sie, dass der Rückruf von Promise.then die Genauigkeit der ersten Runde sicherstellen kann. Wenn das Verhalten beim Fortfahren von .then mit der Browserversion zusammenhängt, verlassen Sie sich während der Entwicklung nicht zu sehr auf die Rückrufsequenz mehrerer .then. Dies ist unzuverlässig .

Wie oben erwähnt, erfolgt das Rendern „am Ende“ einer Ereignisschleife, sodass bei mehreren „Dom ändern“-Vorgängen das letzte Ergebnis zusammengeführt und als Layout-Rendering verwendet wird.

    const btn = document.querySelector(".btn");
    btn.addEventListener("click", () => {
      box.style.transform = "translateX(400px)";
      box.style.transition = "transform 1s ease-in-out";
      box.style.transform = "translateX(200px)";
    });

Der äußere übergeordnete Container hat eine Größe von 400 Pixel. Dieser Code zeigt, dass sich die Box von 0 auf 200 Pixel bewegt und die Aktion zum Festlegen der Box auf 400 Pixel zusammengeführt wird. So realisieren Sie die Box ab 400px, indem Sie das Rendern auf den nächsten Frame verzögern.

Bild

△Demonstrationseffekt

    btn.addEventListener("click", () => {
      box.style.transform = "translateX(400px)";
      requestAnimationFrame(() => {
        requestAnimationFrame(() => {
          box.style.transition = "transform 1s ease-in-out";
          box.style.transform = "translateX(200px)";
        });
      });
    });

„Nested RAF“ sorgt dafür, dass der Callback im nächsten Frame ausgeführt wird. Natürlich können Sie hier auch setTimeout verwenden, um den gleichen Verzögerungseffekt zu erzielen.

Bild

△Demonstrationseffekt nach Verzögerung

04 Aufgabenwarteschlange und Ausführungszeitpunkt

Die JavaScript-Aufgabe wird vor dem Rendern ausgeführt. Wenn die JavaScript-Ausführungszeit innerhalb eines Frames zu lang ist, wird das Rendern blockiert, was ebenfalls zu Frameverlust und Verzögerung führt . Wenn die js-Ausführungszeit hier zu lang ist, handelt es sich um eine lange Aufgabe , die im Folgenden ausführlich vorgestellt werden.

Definition langer Aufgaben: Wenn eine Aufgabe länger als 50 ms dauert , wird sie als lange Aufgabe betrachtet.

Wenn wir über lange Aufgaben sprechen, die zum Einfrieren von Seiten führen, beziehen wir uns normalerweise auf Aufgaben im Haupt-Thread (Haupt-Thread). Der Hauptthread bezieht sich auf den Hauptthread im Renderprozess, der für das Parsen von HTML, das Berechnen von CSS-Stilen, das Ausführen von JavaScript, das Berechnen des Layouts, das Zeichnen von Ebenen und andere Aufgaben verantwortlich ist. Wenn eine Aufgabe (z. B. eine JavaScript-Funktion) im Hauptthread zu lange ausgeführt wird, werden andere Aufgaben im Hauptthread blockiert, einschließlich, aber nicht beschränkt auf, Aktualisierungen der Benutzeroberfläche und die Verarbeitung von Benutzerinteraktionsereignissen, was dazu führt, dass die Seite einfriert oder nicht mehr reagiert.

Die Beziehung zwischen JS-Ausführung und Rendering:

Sowohl JS-Ausführungs- als auch Paint-Aufgaben finden im Hauptthread statt. Die spezifischen Zeichenvorgänge werden vom Synthesethread abgeschlossen, der sich nicht gegenseitig mit dem Hauptthread ausschließt. Wenn jedoch die Ausführungszeit von JS zu lang ist, werden die Daten von Paint kompiliert wird nicht rechtzeitig an die Synthese übermittelt. Thread, daher gibt es Frames auf der Seite, die nicht gezeichnet werden, das heißt, Frames werden gelöscht.

Bild

Bild

△Beziehungsdiagramm zwischen JS-Ausführung und -Rendering

4.1 Warum nicht setTimeout für Animationen verwenden?

Vergleich zwischen raf und setTimeout:

( https://jsfiddle.net/hixuanxuan/mrw6upgs/3/__)

1. Nicht synchron mit der Bildwiederholfrequenz:

Browser aktualisieren normalerweise mit 60 Bildern pro Sekunde, etwa alle 16,67 Millisekunden. Wenn Sie setTimeout zum Erstellen einer Animation verwenden und versuchen, alle 16,67 Millisekunden einen Frame auszuführen, wird Ihr Code nicht vollständig mit der Aktualisierungsrate des Browsers synchronisiert, was zu ausgelassenen Frames führt

2. Verzögerte Ausführung:

Der Verzögerungszeitparameter von setTimeout ist nur eine minimale Verzögerungszeit, keine garantierte genaue Ausführungszeit. Wenn der Hauptthread mit anderen Aufgaben beschäftigt ist, kann sich der setTimeout-Rückruf verzögern, was zu ausgelassenen Frames führt

3.Timer-Zusammenführung:

Das Browser-Rendering verfügt über eine Rendering-Möglichkeit (Rendering-Möglichkeit). Das heißt, der Browser bestimmt anhand des aktuellen Browserkontexts, ob gerendert werden soll, und berücksichtigt dabei Faktoren wie Einschränkungen der Hardware-Aktualisierungsfrequenz, Seitenleistung und ob die Seite einen Hintergrund hat , zwischen Makroaufgaben Kommt nicht unbedingt mit Browser-Zeichnung. Wenn die beiden Aufgaben sehr nahe beieinander liegen, werden sie möglicherweise zu einer Rendering-Aufgabe zusammengeführt und die erzielten Ergebnisse sind unerwartet. Wenn der Aufgabenabstand groß ist, kann er nicht mit der Aktualisierungsfrequenz des Browsers mithalten, was zu einem Frameverlust führt.

Die Ausführungszeit von RAF wird vor dem nächsten Rendering aufgerufen, was bedeutet, dass Sie mit dieser API das DOM ändern können, bevor das nächste Rendering beginnt, und es dann sofort in diesem Rendering widergespiegelt wird, sodass es eine ausgezeichnete Wahl für die Erstellung von Animationen ist .

4.2 Ausführungszeitpunkt von requestIdleCallback

Es wird hauptsächlich ausgeführt, wenn der Hauptthread des Browsers inaktiv ist. Um die Reaktionsfähigkeit sicherzustellen, wird eine Frist (computeDeadline) berechnet, die entscheidet, wann der in requestIdleCallback registrierte Rückruf ausgeführt wird. Hier ein kurzer Überblick über den Algorithmus zur Fristberechnung:

1. Legen Sie die erste Frist fest:

Setzt bei der Initialisierung die Startzeit der letzten Leerlaufzeit der Ereignisschleife auf die aktuelle Zeit.

Legen Sie eine Basisfrist fest, die der Startzeit der letzten Leerlaufzeit der Ereignisschleife plus 50 Millisekunden entspricht (um die Reaktionsfähigkeit auf neue Benutzereingaben sicherzustellen). Der Grund, warum diese 50 ms hinzugefügt werden, liegt darin, dass der Browser darauf vorbereitet ist, einige mögliche plötzliche Benutzerinteraktionen im Voraus zu verarbeiten, wie z. B. Benutzereingabetext. Wenn die angegebene Zeit zu lang ist und Ihre Aufgabe den Hauptthread blockiert, wird auf die Interaktion des Benutzers nicht reagiert. 50 ms stellen sicher, dass Benutzer eine Antwort ohne spürbare Verzögerung erhalten.

2. Überprüfen Sie, ob noch ausstehende Renderings vorliegen:

Initialisieren Sie eine Variable „hasPendingRenders“ auf „false“.

Durchlaufen Sie alle Fenster derselben Ereignisschleife und überprüfen Sie jedes Fenster auf nicht ausgeführte RAF-Rückrufe oder mögliche Rendering-Updates. Wenn ja, setzen Sie hasPendingRenders auf true.

3. Passen Sie die Frist basierend auf dem Timeout an:

Wenn RIC das zweite Parameter-Timeout übergibt, ist die Aktualisierungsfrist Timeout. Dadurch wird der Browser gezwungen, die rIC-Rückruffunktion nach dieser Zeit auszuführen, unabhängig davon, wie beschäftigt er ist.

4. Berücksichtigen Sie die Renderzeit:

Wenn hasPendingRenders „true“ ist, wird der nächste Rendertermin basierend auf der letzten Rendergelegenheitszeit der Ereignisschleife und der aktuellen Aktualisierungsrate berechnet.

Wenn die Frist für das nächste Rendering vor der aktuell festgelegten Frist liegt, ist die Aktualisierungsfrist die Frist für das nächste Rendering.

5. Geben Sie die endgültige Frist zurück:

Gibt die berechnete Frist zurück, anhand derer bestimmt wird, wann der in requestIdleCallback registrierte Rückruf ausgeführt wird.

6. Starten Sie die Ruhephase:

Für jedes Fenster derselben Ereignisschleife wird ein Algorithmus zum „Starten der Leerlaufzeit“ ausgeführt, wobei „computeDeadline“ als Parameter verwendet wird, um zu bestimmen, wann der in „requestIdleCallback“ registrierte Rückruf ausgeführt wird.

Mit anderen Worten, die Berechnung dieser timeRemaining() ist sehr dynamisch und wird auf der Grundlage der oben genannten Faktoren bestimmt.

4.3 Wie implementiert React Time Slice? Was ist der Grund, warum RIC und setTimeout nicht verwendet werden?

Der Grund, warum RIC nicht verwendet wird, liegt darin, dass es in einigen Browsern, wie z. B. Safari, nicht gut funktioniert.

Bedingungen, die erfüllt sein müssen:

1. Unterbrechen Sie die JS-Ausführung und verwenden Sie den Hauptthread, um Aufgaben wie Stil, Layout, Farbe usw. auszuführen, um dem Browser die Möglichkeit zu geben, die Seite zu aktualisieren.

2. Sie können zu einem späteren Zeitpunkt weiterhin Aufgaben planen und Aufgaben ausführen, die beim letzten Mal nicht abgeschlossen wurden.

Der Zweck von React Time Slice besteht darin, die Ausführung des aktuellen js zu unterbrechen und ihm die Ausführung von Rendering-bezogenen Aufgaben zu ermöglichen. Daher wird die erforderliche API nach dem Paint des Browsers ausgeführt, und der Browser stellt keine anderen APIs als RIC bereit. Der Ausführungszeitpunkt von RAF liegt am Ende eines Frames. Zu diesem Zeitpunkt wird eine Makroaufgabe erstellt, um die nächste Task-Runde zu starten. Die Rendering-Aufgabe wird in RAF platziert und in diesem Frame ausgeführt. Wenn Sie setTimeout (fn, 0) zum Erstellen einer Makroaufgabe verwenden und die Verschachtelungsebene des Zeitlimits 5 Ebenen überschreitet, beträgt die Mindestverzögerung 4 ms. Die spezifische Definition des Codes finden Sie in der Timer-Definition von Chrome ( https ://chromium.googlesource .com/chromium/blink/+/master/Source/core/frame/DOMTimer.cpp) , daher wird der Nachrichtenkanal bevorzugt und die Priorität ist höher als setTimeout, das unmittelbar nach dem ausgeführt werden kann Das Rendern des vorherigen Frames ist abgeschlossen, sodass der Effekt einer unterbrochenen JS-Ausführung erzielt werden kann .

4.4 Simulieren Sie die Implementierung von requestIdecallback

Um den Effekt von requestIdecallback zu simulieren, wird die definierte Aufgabenwarteschlange ausgeführt, nachdem der Browser die Rendering-Aufgabe abgeschlossen hat. Darüber hinaus kann sie auch zum Messen der Ausführungszeit der Browser-Rendering-Aufgabe verwendet werden.

Hintergrundaufgaben-API – Web-API Beschreibung | MDN ( https://developer.mozilla.org/zh-CN/docs/Web/API/Background_Tasks_API)

  // 当到时间了,立即执行的函数
  const performWorkUntilDeadline = () => {
    if (scheduledHostCallback !== null) {
      const currentTime = getCurrentTime();
      // 分配任务的剩余时间,这个可执行时间是根据fps动态算的
      deadline = currentTime + yieldInterval;
      const hasTimeRemaining = true;
      // 调用已计划的回调,并传递剩余时间和当前时间。
      const hasMoreWork = scheduledHostCallback(
          hasTimeRemaining,
          currentTime,
        );
        if (!hasMoreWork) {
          isMessageLoopRunning = false;
          scheduledHostCallback = null;
        } else {
          // If there's more work, schedule the next message event at the end
          // of the preceding one.
          port.postMessage(null);
        }
    } else {
      isMessageLoopRunning = false;
    }
    // 给浏览器一个绘制的机会,并重置需要绘制的标志。
    needsPaint = false;
  };
  
 
  const channel = new MessageChannel();
  const port = channel.port2;
  channel.port1.onmessage = performWorkUntilDeadline;

  requestHostCallback = function(callback) {
    scheduledHostCallback = callback;
    if (!isMessageLoopRunning) {
      isMessageLoopRunning = true;
      port.postMessage(null);
    }
  };

05 Interaktive Leistungsindikatoren und Optimierungsmethoden

Die Auswirkung langer Aufgaben auf der Seite führt zu schlechten Erfahrungen wie „steckengeblieben“ und „ausgelassene Frames“. Zu den häufig verwendeten Indikatoren zur Messung der interaktiven Leistung gehören TTI und FID, die mithilfe der Web-Vital-Bibliothek gemessen werden können. Es folgt eine ausführliche Einführung zu den Indikatoren.

5.1 Metriken der interaktiven Leistung

Indikatoren zur Messung der interaktiven Leistung konzentrieren sich hauptsächlich auf die folgenden Aspekte:

5.1.1 TTI (idealer Zeitpunkt zur Interaktion)

1. Interaktivität definieren:

Zunächst müssen wir klären, was „interaktiv“ ist. Eine Seite gilt als interaktiv, was bedeutet, dass der Hauptinhalt der Seite geladen wurde, Benutzer klicken, Eingaben vornehmen und andere interaktive Vorgänge ausführen können und die Seite schnell reagieren kann.

2. Überwachen Sie First Content Paint (FCP) und DOMContentLoaded:

Der Prozess der TTI-Messung beginnt normalerweise mit der Überwachung der Ereignisse First Content Paint (FCP) und DOMContentLoaded. Diese beiden Ereignisse stellen jeweils den Moment dar, in dem der Browser mit dem Zeichnen des Seiteninhalts beginnt und die DOM-Struktur geladen wird.

3. Lange Aufgabenüberwachung:

Lange Aufgaben sind solche, deren Ausführung mehr als 50 Millisekunden dauert. Lange Aufgaben blockieren oft den Hauptthread und verzögern die interaktive Verfügbarkeit der Seite. Durch die Überwachung langer Aufgaben können Sie erfahren, wann der Hauptthread inaktiv wird.

4. Suchen Sie das interaktive Fenster:

Um den TTI zu bestimmen, müssen Sie ein Fenster finden, in dem der Hauptthread mindestens 5 Sekunden lang inaktiv ist. Dieses Fenster sollte nach dem ersten Inhaltszeichnen (FCP) liegen. Während dieses 5-sekündigen Leerlauffensters werden keine längeren Aufgaben ausgeführt, sodass der Benutzer mit der Seite interagieren kann. Sobald dieses Leerlauffenster gefunden ist, notieren Sie den TTI. Wenn keine lange Aufgabe gefunden wird, ist der TTI derselbe wie der FCP.

Bild

△TTI-Messdiagramm (von web.dev)

5.1.2 FID (First Input Delay)

FID oder First Input Delay wird verwendet, um die Reaktionsverzögerung der ersten Interaktion des Benutzers beim Laden der Seite zu quantifizieren. Ein niedriger FID weist darauf hin, dass die Seite schnell auf Benutzerinteraktionen reagiert, während ein hoher FID darauf hinweist, dass die Seite verzögert auf Benutzerinteraktionen reagiert.

1.Ereignisüberwachung:

Um die FID zu berechnen, muss der Browser auf Benutzerinteraktionsereignisse wie Klicks, Tastatureingaben oder Berührungsereignisse warten. Diese Ereignisse werden ausgelöst, wenn der Benutzer mit der Seite interagiert.

2. Bearbeitungszeit des Ereignisses:

Wenn ein Ereignis ausgelöst wird, berechnet der Browser die Zeit vom Auslösen des Ereignisses bis zum Beginn der Verarbeitung des Ereignisses durch den Browser. Dieses Mal ist FID. Dazu gehören die Zeit, die der Browser das Ereignis in die Ereigniswarteschlange stellt, die Wartezeit in der Ereigniswarteschlange und die Zeit, zu der der Browser mit der Verarbeitung des Ereignisses beginnt.

3.Ereignisbehandlung:

Sobald die Verarbeitung des Ereignisses beginnt, zeichnet der Browser den Zeitpunkt auf, zu dem die Verarbeitung gestartet wurde. Wenn die Seite während der Verarbeitung des Ereignisses sehr beschäftigt ist oder andere Aufgaben mit hoher Priorität hat, kann sich die Ereignisverarbeitung verzögern, was zu einer Erhöhung des FID führt.

5.1.3 INP (Interaktion zur nächsten Ziehung)

INP (Interaction to Next Paint) konzentriert sich hauptsächlich auf die Zeitspanne von der Benutzerinteraktion (z. B. Klicken, Scrollen oder Tastenbetätigung) bis zur Seitenreaktion, insbesondere bis zur visuellen Aktualisierung eines bestimmten Elements auf der Seite.

Im Vergleich zu FID, das sich auf die erste Interaktion des Benutzers nach dem Laden der Seite konzentriert , konzentriert sich INP auf die längste Rendering-Verzögerung aller Interaktionen . Daher stellt INP nicht nur den ersten Eindruck dar, sondern kann auch die Antwort umfassend auswerten, wodurch INP besser ist als FID bei der Messung der Benutzerinteraktionserfahrung. Zuverlässiger.

INP wird FID im März 2024 als Standard-Leistungsindikator ersetzen.

Bild

△Die Zeit von der Interaktion bis zum Zeichnen

5.2 So optimieren Sie interaktive Leistungsindikatoren

1. Teilen Sie Aufgaben auf, was eine effektive Möglichkeit ist, lange Aufgaben zu vermeiden.

  • Verwenden Sie die Leistungsanalyse, um lange Aufgaben zu finden

  • Bei langen Aufgaben werden die Aufgaben jedes Schritts aufgeteilt, diejenigen mit hoher Priorität werden ausgeführt und die verbleibenden Teile werden durch Verzögerung der Codeausführung unterbrochen.

Beispielsweise gibt es ein Eingabefeld. Wenn sich der Eingabeinhalt ändert, sind zeitaufwändige Vorgänge wie eine große Anzahl von Berechnungen/DOM-Erstellung erforderlich, was zu einer Eingabeverzögerung führt. Daher müssen wir „dem Haupt-Thread weichen“, wenn der Benutzer „versucht zu interagieren“.

// 通过Promise实现中断后继续执行,setTimeout调用来延迟任务
function yieldToMain () {
  return new Promise(resolve => {
    setTimeout(resolve, 0);
  });
}
    async function saveSettings(tasks) {
      let deadline = performance.now() + 50;

      while (tasks.length > 0) {
        // 判断当前是否有用户交互,isInputPending Chrome87+支持。
        // 可以采用判断Expire Time达到类似效果
        if (
          navigator.scheduling?.isInputPending() ||
          performance.now() >= deadline
        ) {
         // 如果有,退让主线程,等主线程任务完成再回来继续执行。
          await yieldToMain();
          deadline = performance.now() + 50;
          continue;
        }
        const task = tasks.shift();
        task();
      }
    }

    const performLongTask = () => {
       // 创建耗时的任务
      let task = new Array(10000).fill(null).map((_, i) => () => {
        const span = document.createElement("span");
        span.innerText = i;
      });
      saveSettings(task); // 任务切片
    };
    input.addEventListener("input", (e) => {
      input.value = e.target.value;
      performLongTask();
    });

2. Verzögerte Ausführung unkritischer Module. Für Module mit niedrigen Klickraten und Nicht-Kernmodule wird der dynamische Import verwendet und das Neuladen verwendet, oder das Laden wird bis zu einem bestimmten Zeitraum verzögert, um die Aufgaben zu reduzieren, die zunächst vom Hauptthread ausgeführt werden müssen Zeit.

3. Verzögern Sie das Laden von Inhalten, die im Ansichtsfenster nicht sichtbar sind.

  • Verzögertes Laden von Bildern.

  • Stellen Sie das Laden des img-Tags auf „Lazy“ ein, um das Laden der Ressource zu verzögern, bis die Ressource die berechnete Entfernung vom Ansichtsfenster erreicht. Chrome77+ unterstützt dies.

  • Verwenden Sie IntersectionObserver, um zu überwachen, ob sich das Bild im sichtbaren Bereich befindet, und rendern Sie es dann. Es wird empfohlen, Bibliotheken wie lazy-load-image-component ( https://www.npmjs.com/package/react-lazy-load-image-component) zu verwenden .

  • Reduzieren Sie das Rendern einer großen DOM-Menge. Nutzen Sie die Sichtbarkeit von Inhalten, um das Rendern von Elementen außerhalb des Bildschirms zu verzögern. Chrome85+ unterstützt dies.

4. Flexible Caching-Strategie.

  • Verwenden Sie Service-Worker für die standortübergreifende gemeinsame Nutzung von Ressourcen.

Zusätzlich zu den Ressourcen, die mit starkem Caching + ausgehandeltem Caching kombiniert werden können, kann Service-Worker zur Implementierung einer flexibleren Caching-Strategie verwendet werden. Beispielsweise treffen Site A und Site B nur auf denselben Ursprung, aber die Technologie-Stack-Rendering-Methoden sind völlig unterschiedlich. So rufen Sie die Ressourcen von B beim Zugriff auf A vorab ab. Registrieren Sie den Servicemitarbeiter, wenn Site A inaktiv ist. Wenn auf Site B zugegriffen wird, kann der Cache aus dem Cache gelesen werden, um die Ladegeschwindigkeit zu verbessern. sw bietet nicht nur eine gute Leistung beim Caching, sondern kann uns auch dabei helfen, Offline-Anwendungen zu implementieren und manuell Cache zu Dateien hinzuzufügen, die von Browsern nicht stark zwischengespeichert werden können (verschiedene Browser haben unterschiedliche Größenbeschränkungen für Dateien, die stark zwischengespeichert werden können).

Bild

△Verwenden Sie sw, um standortübergreifendes Ressourcen-Prefetching durchzuführen

06 Zusammenfassung

1. Browser sind Multiprozess- und Multithread-Browser. Normalerweise bezieht sich der Hauptthread auf den Hauptthread im Renderprozess.

2. Der Hauptthread kann jeweils nur eine Aufgabe ausführen. Die Zeichnung des Browsers und der Hauptthread schließen sich nicht gegenseitig aus, aber lange Aufgaben führen zu Verzögerungen beim Eintritt in die Synthese, und selbst wenn in diesem Frame keine Synthese stattfindet, wird dies im Frame der Fall sein Fallen gelassen werden.

3. Am Ende jeder Ereignisschleife erfolgt möglicherweise kein Rendern. Der Ausführungszeitpunkt von setTimeout ist nicht stabil.

4. Der Ausführungszeitpunkt von RAF ist am Ende des aktuellen Frames und vor dem Beginn des nächsten Frames stabil, was sich sehr gut für Animationen eignet.

5. Der Ausführungszeitpunkt von RIC ist nicht stabil. Die Berechnungsfrist wird durch die Beeinflussung durch mehrere Faktoren berechnet, die Frist für die Ausführung der Timeout-Steuerung kann jedoch überschritten werden.

6. Verwenden Sie TTI und FID (INP), um die interaktive Leistung der Seite zu messen.

7. Verbessern Sie die interaktive Leistung der Website, indem Sie lange Aufgaben aufteilen, die Ausführung unkritischer Module verzögern, das Laden von Bildern in nicht sichtbaren Bereichen verzögern, das Rendern von Seiten reduzieren und flexible Caching-Strategien konfigurieren.

--ENDE--

Verweise:

[1]HTML-Living-Standard – EVNET-Loop-Verarbeitungsmodell:

https://html.spec.whatwg.org/multipage/webappapis.html#event-loop-processing-model

Literatur-Empfehlungen:

Rekonstruktion des Baidu-Suchanzeigedienstes: Fortschritt und Optimierung

Optimierungspraxis der Baidu APP iOS-Paketgröße 50 MB (7) Compiler-Optimierung

Baidu-Suchinhalt HTAP-Tabellenspeichersystem

Wie sieht im Zeitalter großer Modelle die Baidu-Entwicklerplattform aus, auf der „Jeder KI beherrschen kann“?

Hunderttausende QPS, Baidus Stabilitätsgarantie für die Suche nach heißen Ereignissen

Tang Xiaoou, Gründer von SenseTime, verstarb im Alter von 55 Jahren. Im Jahr 2023 stagnierte PHP . Das Hongmeng-System steht kurz vor der Unabhängigkeit und viele Universitäten haben „Hongmeng-Kurse“ eingerichtet. Die PC-Version von Quark Browser hat mit internen Tests begonnen . ByteDance wurde von OpenAI „verboten“. Das Startup-Unternehmen von Zhihuijun wurde mit einem Betrag von über 600 Millionen Yuan und einer Pre-Money-Bewertung von 3,5 Milliarden Yuan refinanziert. KI-Code-Assistenten sind so beliebt, dass sie nicht einmal in der Programmierung mithalten können Sprachrankings . Das 5G-Modem und die Hochfrequenztechnologie des Mate 60 Pro liegen weit vorne. No Star, No Fix MariaDB spaltet SkySQL ab und gründet sich als unabhängiges Unternehmen
{{o.name}}
{{m.name}}

Supongo que te gusta

Origin my.oschina.net/u/4939618/blog/10322486
Recomendado
Clasificación