Blog

Screenshot http://www.elitepartner.de/mobile

Perfect Google PageSpeed Insights Score – der mobilen Website von ElitePartner Beine machen

Im Moment arbeite ich bei meinem Kunden ElitePartner an seiner neuer mobilen Website. Deren erste Version berücksichtigte bereits die meisten Punkte, die eine performanceoptimierte Site aufweisen sollte.
Dies sind im Allgemeinen:

  • Landing-Page-Redirects werden vermieden
  • GZIP-Kompression ist aktiviert
  • Cache-Header für statische Inhalte sind gesetzt, Ressourcen sind gefingerprinted / revisioniert
  • CSS ist minifiziert
  • HTML ist minifiziert
  • JS ist minifiziert
  • sichtbarer Content (above-the-fold content) wird geladen, bevor Content, der sich nicht im sichtbaren Bereich befindet (below-the-fold content), geladen wird.
  • TTFB – der Server antwortet sehr schnell (rein statischer Content, Dynamik wird über SPA/REST realisiert)

Dies ermöglichte unter anderem ein Grunt-Build-Script, das die Best-Practices der Performance-Optimierung berücksichtigt. Wie ein solches Build-Script aussehen kann, habe ich bereits in der WEAVE 03/2013 unter meinem Pseudonym Oliver Fuchs veröffentlicht.

Leider werden in diesem Build-Script wichtige Punkte der Performance-Optimierung nicht berücksichtigt. Sowohl JavaScript- als auch CSS-Ressourcen blockieren das Rendering des sichtbaren Contents. Dies führt zu einem Google PageSpeed Insights-Ranking von nur rund 75%.

76 Punkte

Das konnte ich natürlich nicht auf mir sitzen lassen. Um das Google PageSpeed Insights-Ranking auf 100% zu erhöhen, habe ich folgende Engineering Tasks umgesetzt:

JavaScript defered laden

Für JavaScript gibt es die Möglichkeit, Scripte sowohl asynchron als auch defered zu laden. Wir haben uns für letztere Möglichkeit entschieden.

Unser Build-Script fasst JS-Ressourcen zusammen (Konkatenierung) und verkleinert diese (Minifizierung). Dazu verwenden wir UseMin. Ein defered Loading von JS-Resourcen unterstützt die aktuell stabile Version von UseMin leider noch nicht. Das kommt jedoch mit der Version 2.0, die sich bereits verwenden lässt und bei uns nur wenige Probleme bereitet hat (lediglich für SourceMaps mussten wir ein wenig patchen).

<!-- build:js resources/js/angular-complete.js -->
  <script src="./resources/components/angular/angular.js" defer></script>
  <script src="./resources/components/angular-animate/angular-animate.js" defer></script>
  <script src="./resources/components/angular-route/angular-route.js" defer></script>
  <script src="./resources/components/angular-touch/angular-touch.js" defer></script>
  <script src="./resources/components/angular-resource/angular-resource.js" defer></script>
  <script src="./resources/components/angular-cachebuster/cacheBuster.js" defer></script>
  <script src="./resources/components/angular-md5/angular-md5.js" defer></script>
<!-- endbuild -->

UseMin generiert Optimierungs-Tasks für das eigene Build-Script, indem es zu optimierende Ressourcen parst. Zusammengefasste Scripte lassen sich defered laden, indem sämtliche zusammenzufassende Scripte das defered-Attribut tragen. Falls nur einzelne Scripte ein defered-Attribut tragen, schlägt der Build fehl.

CSS asynchron laden

Für CSS gibt es leider (noch) kein defered-Attribut. Auch Resource Priorities – ein kommender W3C-Standard (lazyload="1") – werden noch nicht von gängigen Browsern unterstützt.

Daher benutze ich einen Trick von Ilya Grigorik, den er auf einem Web Performance Meetup hier in Hamburg verriet.

Chrome und die meisten WebKit-basierten Browser laden CSS-Dateien auch dann, wenn sie nicht für das jeweilige Gerät bestimmt sind. Wir wählen als Gerät ein fiktives Lazy-Gerät (media=lazy).

<link rel="stylesheet" href="./css/apps.css" media="lazy" id="app-css">

Allerdings werten diese Browser das CSS nicht aus und blockieren damit auch das Rendering der Site nicht. Damit Inhalte bereits beim Laden dargestellt werden, sind (wenige) benötigte CSS-Anweisungen direkt inline in den Header der Site integriert, um den sichtbaren Content der Startseite darzustellen.

.emn-app-mobile {
	visibility:hidden;
}
.emn-initial-loading-container {
	width: 100%;
	height: 100%;
	background-image: url('resources/images/background_login-reg_start.jpg');
	background-image: -webkit-image-set(url('resources/images/background_login-reg_start.jpg') 1x, url('resources/images/retina/background_login-reg_start@2x.jpg') 2x);
	background-repeat: no-repeat;
	background-position: top center;
	position: absolute;
	top:0;
	left:0;
	display:block;
}
body {
	background-image: -webkit-gradient(linear, 50% 0, 50% 100%, color-stop(0%, #002444), color-stop(100%, #346a9a));
	background-image: -webkit-linear-gradient(#002444, #346a9a);
	background-image: -moz-linear-gradient(#002444, #346a9a);
	background-image: -o-linear-gradient(#002444, #346a9a);
	background-image: linear-gradient(#002444, #346a9a);
	background-repeat: no-repeat;
	background-color: #346a9a;
}
html, body {
	height: 100%;
	font-family: "Roboto", "Helvetica", "Helvetic Neue", "Arial", sans-serif;
}

Um ein FOUC („Flash Of Unstyled Content“) während des Ladens der Site zu verhindern, sind ungestylte Bereiche der Site (.emn-app-mobile) unsichtbar.

Sobald die Site geladen ist, wird das media-Attribut des Stylesheets per JavaScript verändert.

    document.getElementById('app-css').media = 'screen';

Dies führt dazu, dass das CSS erst nach dem Laden der Site ausgewertet wird. Im geladenen CSS wird natürlich der nun gestylte Bereich der Site (.emn-app-mobile) wieder sichtbar gemacht.

Durch diese Optimierungsmaßnahmen hat sich das Google PageSpeed Insights-Ranking auf 100/100 Punkte erhöht.

100 Punkte

Über den Autor

Oliver Ochs

Oliver baut Websites seit 1998 und verbringt seitdem nahezu jeden Tag damit, das Web besser, schneller und einfacher zu machen. Er gibt dazu Coachings, veranstaltet Workshops und arbeitet in Projekten. Dabei liebt er es, komplexe Performance- und Front-End-Probleme zu lösen. Er ist ein Frontend-Op, der sich als Full-Stack-Developer in Java und JS an der Schnittstelle zwischen UI und Backend am wohlsten fühlt.

2 Kommentare

  1. So wie Du es beschreibst, scheint es nur bei vergleichsweise „kleinen“ Dateien zu funktionieren…

    Nach dem ich es genau so wie beschrieben „umgemogelt“ habe, hatte ich eine Meldung, das nur 4% der gesamten Seite im „above the fold“ sind, und ich doch bitte die css Dateien „anpassen“ soll..

    Per Javascript den media-typ zu wechseln funktioniert anstandslos.

  2. Das CSS für „above the fold“-Content liegt inline. Daher blockiert er nicht. Der Trick sorgt dafür, dass „below the fold“-CSS nachgeladen wird. Wie ist denn die URL für die betroffene Site?

Antwort hinterlassen