Authoring Critical Above-the-Fold CSS

Avatar of Ben Edwards
Ben Edwards on

DigitalOcean bietet Cloud-Produkte für jede Phase Ihrer Reise. Starten Sie mit 200 $ kostenlosem Guthaben!

Der folgende Beitrag stammt von Ben Edwards. Ich habe gesehen, wie Ben über ein einfaches Sass @mixin getwittert hat, das es ermöglichte, CSS-Teile als "kritisch" zu bezeichnen – die Idee ist, dieses kritische CSS zuerst zu laden und den Rest des CSS später zu laden. Eine clevere Idee, und eine, die in der Web-Performance-Szene sehr beliebt wird. Ich dachte, ich lasse Ben diese Ideen ausführlicher für uns vorstellen.

Google PageSpeed Insights und meine Webseiten; es war eine himmlische Verbindung, bis sich die Dinge änderten… PageSpeed begann mir zu sagen, dass ich meine CSS-Auslieferung optimieren müsste, dass meine CSS-Dateien rendern-blockieren, dass kein Inhalt oberhalb des "Fold" meiner Seite ohne das Warten auf das Laden dieser Dateien gerendert werden könnte, und dass ich die kritischen Teile dieser Dateien direkt in mein HTML einfügen sollte.

Geh nach Hause, PageSpeed, rief ich, wer in seinem rechten Verstand will schon einen Haufen CSS in seinem HTML? Ich bin ein legitimer Profi, ich habe einen Workflow, wussten Sie das? Ich spottete.

Das war lange mein Standpunkt, bis ich den folgenden Tweet las

Ich würde gerne eine Seite wie CSS Zen Garden sehen, aber wo Entwickler versuchen,
die gleiche responsive Seite besser auf webpagetest.org abschneiden zu lassen

Scott Jehl

Ich habe mich schon lange dazu verpflichtet, meinen Webseiten die bestmöglichen Ergebnisse von webpagetest.org zu verschaffen, und das erforderte eine Änderung des Workflows. Warum sollte ich ihn also nicht für PageSpeed ändern? Wenn Sie bereits das mod_pagespeed-Modul von Google verwenden, lehnen Sie sich zurück und klopfen Sie sich auf die Schulter, denn das Modul hat Sie abgedeckt. Für diejenigen unter Ihnen, die wie ich nicht dazu gehören, hier ist, wie ich vorgegangen bin.

Jetzt kommt die Wissenschaft

Um das Problem zu lösen, musste ich zunächst verstehen, was PageSpeed mir sagte. Externe Stylesheets (lesen Sie die, die über link-Tags eingebunden sind) blockieren das Rendern. Das bedeutet, dass der Browser erst dann Inhalte auf dem Bildschirm anzeigt, wenn Ihr gesamtes CSS heruntergeladen wurde. Kombinieren Sie dies mit der Tatsache, dass, wenn die zur Darstellung der Seite benötigte Datenmenge das anfängliche Congestion Window (typischerweise 14,6 kB komprimiert) überschreitet, zusätzliche Roundtrips zwischen Server und Browser des Benutzers erforderlich sind. Dies alles führt zu zusätzlicher Netzwerklatenz und kann für Benutzer mit Netzwerken hoher Latenz, wie z. B. Mobilgeräte, zu erheblichen Verzögerungen beim Laden der Seite führen.

Die Empfehlung von PageSpeed ist, Ihr CSS in zwei Teile zu teilen; einen Inline-Teil, der für das Styling des Inhalts oberhalb des "Fold" verantwortlich ist, und den Rest, der verzögert werden kann. Bevor wir uns nun aufhängen, ob der "Fold" existiert oder nicht, lassen Sie uns einfach zustimmen, dass alles, was wir tun können, um unsere Daten so schnell wie möglich zu unseren Nutzern zu bringen, eine gute Sache ist, oder?

Bestimmen, was kritisch ist

Um zu bestimmen, welche Teile unseres CSS kritisch sind, mussten wir unsere Webseiten in "mobilen" und "Desktop"-Größen inspizieren und dann einen Schnappschuss der CSS-Regeln erstellen, die auf die im Viewport sichtbaren Elemente angewendet wurden. Das schien eine entmutigende Aufgabe zu sein, aber keine Angst, einige sehr kluge Leute waren da, um zu helfen

Mit den Ergebnissen des Inspektionsprozesses bewaffnet, muss ich nun mein HTML ändern, um mein CSS nicht rendern-blockierend zu laden.

Eine asynchrone Last von meinen Schultern

Stellen wir uns vor, eines meiner HTML-Dokumente sähe so aus

<html>
  <head>
    <link rel="stylesheet" href="things.css">
  </head>
  <body>
    <div class="thing1">
      Hello world, how goes it?
    </div>
    ...
    <div class="thing2">
      Hey, I'm totally below-the-fold
    </div>
  </body>
</html>

Ebenso enthielte things.css Folgendes

.thing1 { color: red; }
.thing2 { background: green; }

Mit den Ergebnissen des Inspektionsprozesses kann ich nun den kritischen, oberhalb des "Fold" liegenden Teil meines CSS im head wie folgt einfügen

<html>
  <head>
    <style>
      .thing1 { color: red; }
    </style>
  </head>
  <body>
    <div class="thing1">
      Hello world, how goes it?
    </div>
    ...

Kombinieren Sie dies mit Filament Group's loadCSS, und ich kann das verbleibende unterhalb des "Fold" liegende CSS asynchron wie folgt laden

    ...
    <div class="thing2">
      Hey, I'm totally below-the-fold
    </div>
    <script>
      /*!
      Modified for brevity from https://github.com/filamentgroup/loadCSS
      loadCSS: load a CSS file asynchronously.
      [c]2014 @scottjehl, Filament Group, Inc.
      Licensed MIT
      */
      function loadCSS(href){
        var ss = window.document.createElement('link'),
            ref = window.document.getElementsByTagName('head')[0];

        ss.rel = 'stylesheet';
        ss.href = href;

        // temporarily, set media to something non-matching to ensure it'll
        // fetch without blocking render
        ss.media = 'only x';

        ref.parentNode.insertBefore(ss, ref);

        setTimeout( function(){
          // set media back to `all` so that the stylesheet applies once it loads
          ss.media = 'all';
        },0);
      }
      loadCSS('things.css');
    </script>
    <noscript>
      <!-- Let's not assume anything -->
      <link rel="stylesheet" href="things.css">
    </noscript>
  </body>
</html>

Ein Workflow für die Zukunft

Ausgezeichnete Nachrichten! PageSpeed ist begeistert! Es beschwert sich nicht mehr über rendern-blockierendes CSS und ist zufrieden, dass der Inhalt oberhalb des "Fold" die verdiente Priorität erhalten hat, aber in dieser modernen Welt der CSS-Präprocessor und Front-End-Tools reicht ein manueller Prozess wie der oben beschriebene einfach nicht aus.

Ein automatisierter Ansatz

Wer von Ihnen nach einem automatisierten Ansatz im Stil von mod_pagespeed sucht und auch mit Node vertraut ist (Entschuldigung an diejenigen, die es nicht sind, aber hier bei Clock ist es ein wichtiger Bestandteil von allem, was wir tun) wird sich definitiv Penthouse und Addy Osmani's experimentelles Node-Modul, Critical, ansehen wollen, die beide Mittel zum Inline-Einbinden oder Manipulieren von kritischem CSS bieten, wie es über die PageSpeed API ermittelt wird. Nun, während ein vollständig automatisierter Workflow wie der Himmel klingt, ist die eine Sache, die mich bei den aktuellen Tools stört, dass sie nicht berücksichtigen, dass jegliche inline eingebundenen CSS-Regeln erneut ausgeliefert werden, sobald das CSS unterhalb des "Fold" heruntergeladen wurde. Und im Sinne der Übermittlung von so wenig Daten wie nötig an unsere Nutzer, fühlt sich dies wie eine unnötige Duplizierung an.

CSS-Präprozessoren zur Rettung

Die Nutzung Ihres bevorzugten CSS-Präprozessors für die Erstellung von CSS oberhalb und unterhalb des "Fold" erscheint mir als ein No-Brainer und ist etwas, das das Front-End-Team derzeit bei Clock experimentiert.

Neue Projekte eignen sich sehr gut für diesen Ansatz, und kritisches und nicht-kritisches CSS könnte über gut strukturierte @import-Regeln erstellt werden

/* critical.scss - to be in-lined */
@import "header";
/* non-critical.scss - to be asynchronously loaded */
@import "web-fonts";
@import "footer";

Sollten sich Ihre Partials nicht für diese Art der Strukturierung eignen, kann das Team Sass's bedingte Stile Compass-Plug-in Jacket sehr nützlich sein. Wenn beispielsweise Ihr Partial _shared.scss Regeln für Elemente oberhalb und unterhalb des "Fold" enthielte, könnten die kritischen und nicht-kritischen Regeln von Jacket wie folgt umschlossen werden

@include jacket(critical) {
  .header {
    color: red;
  }
}

@include jacket(non-critical) {
  @include font-face(...);
  ...

 .footer {
    color: blue;
  }
}

Dann könnten critical.css und non-critical.css wie folgt bearbeitet werden, um dasselbe CSS zu erhalten

/* critical.scss - to be in-lined */
$jacket: critical;
@import "shared";
/* non-critical.scss - to be asynchronously loaded */
$jacket: non-critical;
@import "shared";

Dieser Ansatz scheint auch mit der Art und Weise übereinzustimmen, wie viele in der Community Media Queries auf Komponentenebene statt an globaler Stelle erstellen, und könnte gegebenenfalls verwendet werden, um kritische und nicht-kritische CSS-Regeln auf Komponentenebene zu definieren.

Wir arbeiten diese Dinge noch aus

Obwohl das Update der Webversion von PageSpeed Insights nun fast ein Jahr alt ist, habe ich das Gefühl, dass das Thema kritisches CSS und die Priorisierung von oberhalb des "Fold" liegendem Inhalt erst in den letzten Monaten an Bedeutung gewonnen hat.

Ich hoffe, dass ich Sie durch Einblicke in die Art und Weise, wie ich die Erstellung gehandhabt habe, dazu anrege, es in Ihren Workflow zu integrieren. Und achten Sie genau auf die oben aufgeführten Werkzeuge, da die meisten sich in frühen Entwicklungsstadien befinden und ich auf spannende Veränderungen hoffe.