Mehr Interaktivität mit JavaScript

Ilya Grigorik
Ilya Grigorik

Veröffentlicht: 31. Dezember 2013

Mit JavaScript können wir fast jeden Aspekt der Seite ändern: Inhalt, Stil und Reaktion auf Nutzerinteraktionen. JavaScript kann jedoch auch DOM-Konstruktion und Verzögerung beim Rendern der Seite blockieren. Für eine optimale Leistung sollten Sie Ihr JavaScript asynchron ausführen und unnötiges JavaScript aus dem kritischen Renderingpfad entfernen.

Zusammenfassung

  • JavaScript kann das DOM und das CSSOM abfragen und ändern.
  • JavaScript-Ausführungsblöcke im CSSOM
  • JavaScript blockiert die DOM-Erstellung, es sei denn, sie wird explizit als asynchron deklariert.

JavaScript ist eine dynamische Sprache, die in einem Browser ausgeführt wird und es uns ermöglicht, nahezu jeden Aspekt des Seitenverhaltens zu ändern: Wir können Inhalte ändern, indem wir Elemente zum DOM-Baum hinzufügen und daraus entfernen. können wir die CSSOM-Eigenschaften jedes Elements ändern. Benutzereingaben verarbeiten können. und vieles mehr. Sehen wir uns zur Veranschaulichung an, was passiert, wenn die vorherige "Hallo Welt"- wird ein kurzes Inline-Skript hinzugefügt:

<!DOCTYPE html>
<html>
  <head>
    <meta name="viewport" content="width=device-width,initial-scale=1" />
    <link href="style.css" rel="stylesheet" />
    <title>Critical Path: Script</title>
  </head>
  <body>
    <p>Hello <span>web performance</span> students!</p>
    <div><img src="awesome-photo.jpg" /></div>
    <script>
      var span = document.getElementsByTagName('span')[0];
      span.textContent = 'interactive'; // change DOM text content
      span.style.display = 'inline'; // change CSSOM property
      // create a new element, style it, and append it to the DOM
      var loadTime = document.createElement('div');
      loadTime.textContent = 'You loaded this page on: ' + new Date();
      loadTime.style.color = 'blue';
      document.body.appendChild(loadTime);
    </script>
  </body>
</html>

Ausprobieren

  • Mit JavaScript können wir in das DOM eindringen und die Referenz auf den ausgeblendeten Span-Knoten abrufen. Der Knoten ist möglicherweise nicht im Renderbaum sichtbar, aber er ist immer noch im DOM vorhanden. Wenn wir die Referenz haben, können wir den Text (über .textContent) ändern und sogar die berechnete Eigenschaft „displayStyle“ von „none“ zu „inline“ überschreiben. Jetzt wird auf unserer Seite Hallo interaktive Schüler! angezeigt.

  • Mit JavaScript können wir auch neue Elemente im DOM erstellen, stylen, anhängen und entfernen. Technisch gesehen könnte die gesamte Seite nur aus einer einzigen großen JavaScript-Datei bestehen, die die Elemente einzeln erstellt und mit einem Stil versehen. Das würde zwar funktionieren, in der Praxis ist es jedoch viel einfacher, HTML und CSS zu verwenden. Im zweiten Teil unserer JavaScript-Funktion erstellen wir ein neues Div-Element, legen den Textinhalt fest, formatieren es und hängen es an den Body an.

Eine Vorschau einer Seite, die auf einem Mobilgerät gerendert wird.

Damit haben wir den Inhalt und den CSS-Stil eines vorhandenen DOM-Knotens geändert und dem Dokument einen ganz neuen Knoten hinzugefügt. Unsere Seite wird keine Designpreise gewinnen, aber sie veranschaulicht die Leistungsfähigkeit und Flexibilität von JavaScript.

JavaScript bietet uns zwar viel Leistung, aber es gibt viele zusätzliche Einschränkungen im Hinblick darauf, wie und wann die Seite gerendert wird.

Im vorherigen Beispiel befindet sich das Inline-Skript unten auf der Seite. Warum? Sie sollten es selbst ausprobieren, aber wenn wir das Skript über das <span>-Element verschieben, werden Sie feststellen, dass das Skript fehlschlägt und sich beschwert, dass es keinen Verweis auf <span>-Elemente im Dokument finden kann. Das heißt, getElementsByTagName('span') gibt null zurück. Das zeigt eine wichtige Eigenschaft: Unser Script wird genau an der Stelle ausgeführt, an der es in das Dokument eingefügt wurde. Wenn der HTML-Parser ein Script-Tag findet, hält er den Aufbau des DOM an und übergibt die Kontrolle an die JavaScript-Engine. Nach Abschluss der Ausführung der JavaScript-Engine setzt der Browser dort fort, wo er aufgehört hat, und fährt mit dem DOM-Aufbau fort.

Mit anderen Worten: Unser Skriptblock kann später auf der Seite keine Elemente finden, da sie noch nicht verarbeitet wurden. Anders ausgedrückt: Die Ausführung unseres Inline-Scripts blockiert die DOM-Erstellung, was auch das erste Rendern verzögert.

Eine weitere subtile Eigenschaft von Skripts in unsere Seite ist, dass sie nicht nur das DOM, sondern auch die CSSOM-Eigenschaften lesen und ändern können. Genau das passiert in unserem Beispiel, wenn wir die Anzeigeeigenschaft des span-Elements von none in inline ändern. Das Ergebnis? Wir haben jetzt eine Race-Bedingung.

Was passiert, wenn der Browser beim Ausführen des Skripts das CSSOM nicht vollständig heruntergeladen und erstellt hat? Die Antwort ist nicht gerade gut für die Leistung: Der Browser verzögert die Skriptausführung und die DOM-Erstellung, bis der Download und die Erstellung des CSSOM abgeschlossen sind.

Kurz gesagt: JavaScript führt viele neue Abhängigkeiten zwischen dem DOM, dem CSSOM und der JavaScript-Ausführung ein. Dies kann zu erheblichen Verzögerungen bei der Verarbeitung und Darstellung der Seite auf dem Bildschirm im Browser führen:

  • Der Speicherort des Scripts im Dokument ist wichtig.
  • Wenn der Browser auf ein Skript-Tag stößt, wird die DOM-Erstellung pausiert, bis das Skript fertig ausgeführt ist.
  • Mit JavaScript können das DOM und das CSSOM abgefragt und geändert werden.
  • Die JavaScript-Ausführung wird pausiert, bis das CSSOM bereit ist.

„Optimierung des kritischen Rendering-Pfads“ im Wesentlichen bezieht sich auf das Verständnis und die Optimierung des Abhängigkeitsdiagramms zwischen HTML, CSS und JavaScript.

Parser-Blockierung im Vergleich zu asynchronem JavaScript

Standardmäßig wird JavaScript-Code „parser-blockierend“ ausgeführt: Wenn der Browser ein Script im Dokument findet, muss er die DOM-Erstellung pausieren, die Kontrolle an die JavaScript-Laufzeit übergeben und das Script ausführen, bevor er mit der DOM-Erstellung fortfährt. Das haben wir in unserem vorherigen Beispiel mit einem Inline-Script gesehen. Tatsächlich blockieren Inline-Skripts immer den Parser, es sei denn, Sie schreiben zusätzlichen Code, um ihre Ausführung zu verzögern.

Was ist mit Skripts, die mit einem Skript-Tag enthalten sind? Extrahieren Sie den Code aus dem vorherigen Beispiel in eine separate Datei:

<!DOCTYPE html>
<html>
  <head>
    <meta name="viewport" content="width=device-width,initial-scale=1" />
    <link href="style.css" rel="stylesheet" />
    <title>Critical Path: Script External</title>
  </head>
  <body>
    <p>Hello <span>web performance</span> students!</p>
    <div><img src="awesome-photo.jpg" /></div>
    <script src="app.js"></script>
  </body>
</html>

app.js

var span = document.getElementsByTagName('span')[0];
span.textContent = 'interactive'; // change DOM text content
span.style.display = 'inline'; // change CSSOM property
// create a new element, style it, and append it to the DOM
var loadTime = document.createElement('div');
loadTime.textContent = 'You loaded this page on: ' + new Date();
loadTime.style.color = 'blue';
document.body.appendChild(loadTime);

Ausprobieren

Unabhängig davon, ob wir ein <script>-Tag oder ein Inline-JavaScript-Snippet verwenden, sollten beide dasselbe Verhalten zeigen. In beiden Fällen pausiert der Browser führt das Skript aus, bevor es den Rest des Dokuments verarbeiten kann. Im Falle einer externen JavaScript-Datei muss der Browser jedoch pausieren, Warten Sie, bis das Skript von der Festplatte, dem Cache oder einem Remote-Server abgerufen wird. kann das kritische Rendering im Hinblick auf Pfad.

Standardmäßig wird JavaScript durch den Parser blockiert. Da der Browser nicht weiß, was das Skript auf der Seite tun soll, geht er vom Worst-Case-Szenario aus und blockiert den Parser. Wenn dem Browser signalisiert wird, dass das Script nicht genau an der Stelle ausgeführt werden muss, an der es referenziert wird, kann der Browser das DOM weiter erstellen und das Script ausführen, wenn es bereit ist, z. B. nachdem die Datei aus dem Cache oder von einem Remote-Server abgerufen wurde.

Dazu wird dem <script>-Element das async-Attribut hinzugefügt:

<!DOCTYPE html>
<html>
  <head>
    <meta name="viewport" content="width=device-width,initial-scale=1" />
    <link href="style.css" rel="stylesheet" />
    <title>Critical Path: Script Async</title>
  </head>
  <body>
    <p>Hello <span>web performance</span> students!</p>
    <div><img src="awesome-photo.jpg" /></div>
    <script src="app.js" async></script>
  </body>
</html>

Ausprobieren

Wenn Sie dem Script-Tag das Keyword „async“ hinzufügen, wird der Browser angewiesen, die DOM-Erstellung nicht zu blockieren, während er auf die Verfügbarkeit des Scripts wartet. Dies kann die Leistung erheblich verbessern.

Feedback