Modernizr trifft LocalStorage
von Martin Waldau
Modernizr ist, grob gesagt, ein Tool zur Feature-Erkennung von Browsern. Es testet die verschiedenen Fähigkeiten und schreibt sie ins DOM. Damit kann eine Webseite auf die Art von Browser und Umgebung reagieren, in der sie dargestellt wird.
Darüber hinaus bietet Modernizr noch mehr Möglichkeiten, aber um die soll es hier nicht gehen.
Ich möchte mich heute mit der Performance von Modernizr beschäftigen. Aber bevor ich damit beginne, kurz zur Funktionsweise von Modernizr. Es ist ein Tool, das in den <head>-Bereich einer Webseite eingebunden wird und bei jedem Aufruf einer Seite ausgeführt wird. Es prüft die Fähigkeiten des Browsers mittels Javascript und schreibt dann entsprechende Klassen-Attribute in das <html>-Tag.
Nun würde es doch reichen, wenn Modernizr pro Nutzer-Session nur einmal geladen und ausgeführt wird? Denn üblicherweise ändert ein Browser während einer Session nicht seine Fähigkeiten. Würde ich die Klassen zwischenspeichern, könnte ich mir einen erneuten Test sparen. Aber wie viel Zeit und CPU würde ich dadurch sparen? Dazu schaue ich mir einmal an, wie lange Modernizr für seine Feature-Detection braucht. Die Ladezeiten vernachlässige ich hierbei, da das Script so oder so geladen werden müsste. Bei weiteren Seitenaufrufen würde es somit aus dem Browser-Cache kommen.
Ausgangslage
Auf der Referenzseite wird nur Modernizr geladen, sonst nichts. Das HTML-Dokument ist auch sonst weitgehend leer, um störende Einflüsse zu minimieren. Exemplarisch nehme ich mir die Browser Chrome 24, Firefox 18.0.2 und Safari 6.0.2 um die Zeiten (reine Laufzeit des Javascripts) zu bestimmen. Testumgebung ist ein 2011er MacBook Pro.
Das HTML-Dokument:
<!DOCTYPE HTML>
<html lang="de">
<head>
<title>Modernizr - Performance optimization</title>
<meta charset="utf-8">
<script src="js/modernizr.custom.49354.js"></script>
</head>
<body>
<h1>Modernizr - Performance Optimization</h1>
</body>
</html>
Die Zeiten, die für die Ausführung von Modernizr benötigt werden, schauen sehr schnell aus:
- Chrome 24: 20 ms
- Firefox 18.0.2: 11 ms
- Safari 6.0.2: 8 ms
LocalStorage - oder auch DOM Storage, Web Storage, HTML5 Storage
Viele Namen, aber dahinter verbirgt sich immer das Gleiche. Bevor es weiter geht ein paar Details zu LocalStorage.
Wollte man bisher Daten über einen Seitenrequest hinweg im Client speichern, hatte man nur die Möglichkeit die Daten in einem Cookie zu hinterlegen. Aber die Größe von Cookies ist begrenzt und man kann auch nur Text darin speichern. Es fehlte ein vernünftiger und persistenter Speicher für Web-Applikationen. Das bietet nun LocalStorage. Im Grunde handelt es sich dabei einen Key-Value-Store. LocalStorage wird von allen modernen Browsern (inkl. mobile) und sogar dem IE (seit Version 8) unterstützt. Auf die Daten zugegriffen wird wie bei Key-Value-Stores üblich mittels getItem()
- und setItem()
-Funktionen. Dabei wird ein selbst gewählter Schlüssel zur Identifizierung der Daten benutzt. Als Value lässt sich von einfachen Werten bis zu ganzen Objekten alles speichern. Außerdem lassen sich Events an den LocalStorage binden, um Änderungen überwachen zu können. Mehr Informationen und Details zur Implementierung gibts auf diveintohtml5.com.
Optimierung mit LocalStorage
Tja, bei solchen Zeiten lohnt sich Optimieren ja eigentlich nicht mehr. Auf die gesamte Webseite betrachtet, finden sich sicher lohnendere Elemente, die verbessert werden können. Aber wenn wir schon dabei sind, wie würde es aussehen, wenn die angesprochene Lösung mit LocalStorage umgesetzt würde?
Das HTML-Dokument ändert sich kaum:
<!DOCTYPE HTML>
<html lang="de">
<head>
<title>Modernizr - Performance optimization</title>
<meta charset="utf-8">
<script src="js/localstorage.js"></script>
</head>
<body>
<h1>Modernizr - Performance Optimization</h1>
</body>
</html>
Aber anstatt Modernizr nun direkt zu laden, wird ein Javascript zwischengeschaltet, das auf den LocalStorage prüft:
if (localStorage)
{
var classes = localStorage.getItem('modernizr.classes');
if (classes != null)
{
var html = document.getElementsByTagName('html')[0];
html.className = classes;
}
else
{
loadScript('js/modernizr.custom.49354.js', getAndStoreClasses);
}
}
else
{
loadScript('js/modernizr.custom.49354.js', '');
}
function getAndStoreClasses()
{
var html = document.getElementsByTagName('html')[0];
var classes = html.className;
localStorage.setItem('modernizr.classes', classes);
}
function loadScript(resource, callbackFunction)
{
var head = document.getElementsByTagName('head')[0];
var scriptTag = document.createElement('script');
scriptTag.type = 'text/javascript';
scriptTag.src = resource;
scriptTag.onload = callbackFunction;
scriptTag.onreadystatechange = callbackFunction;
head.appendChild(scriptTag);
}
Es bedarf nun einer Funktion die Modernizr dynamisch nachlädt. Das erledigt hier für uns „loadScript()“. Dieser Funktion kann ein Callback-Aufruf mitgegeben werden. Das nutzen wir und geben ihr die Funktion mit, die die Daten im LocalStorage speichert. Sollte LocalStorage nicht verfügbar sein, wird das Modernizr-Script „normal“ geladen.
Hier die Testergebnisse mit LocalStorage (beim zweiten Aufruf der Seite):
- Chrome 24: 5 ms
- Firefox 18.0.2: 2 ms
- Safari 6.0.2: 2 ms
Erwartungsgemäß ist das Zwischenspeichern schneller als die Tests bei jedem Seitenaufruf auszuführen. Ab der 2. Seite die geladen wird, dauert es mit LocalStorage ca. 2-5 ms.
Aufgrund der sehr hohen Geschwindigkeit von Modernizr ist der Performancegewinn hier gering. Aber als Proof-Of-Concept zeigt sich, dass diese Methode durchaus Sinn hat. Vor allem dann, wenn wiederholt Operationen/Berechnungen im Client ausgeführt werden, müssen die auf statischen Daten beruhen. Dies kann für Browsergames genauso wie für größere Web-Applikationen von Interesse sein.