HTML5 in der Praxis

Bessere Performance mit HTML5

11.04.2011 von Sven Hähle
Richtig eingesetzt, verbessert HTML5 die Performance, die Sicherheit und die Stabilität von Webapplikationen.

Wenn im Zusammenhang mit HTML5 von Performance die Rede ist, dann geht es im Wesentlichen um drei Dinge: grafische (Layout-)Elemente, deren Animation sowie die Videointegration. Noch kürzer könnte man sagen: Es geht ums Visuelle.

Noch ist HTML5 ein Working Draft, kein Standard: Dafür gibt es bereits ein eigenes Logo für das neue HTML - und in der Praxis werden immer mehr Websites auf HTML5 umgestellt. Die allermeisten Neuentwicklungen setzen sowieso auf HTML5.

Das Erfolgstrio HTML5, CSS3 und JavaScript gibt Entwicklern hervorragende Werkzeuge an die Hand, um das optische Erscheinungsbild von Webapplikationen zu verbessern und zugleich deutliche Performance-Gewinne zu erreichen.

In diesem Artikel konzentrieren wir uns auf Layout, Grafik und Animation, das viel diskutierte Thema Video-Integration mittels HTML5 lassen wir an dieser Stelle einmal außen vor. Dass HTML5 auf diesem Gebiet bahnbrechende Neuerungen bringt, dürfte den meisten Entwicklern bekannt sein. Ansonsten bietet Ihnen der Beitrag HTML5 - Neuerungen bei Video, Audio und Vektorgrafik diesbezüglich weitergehende Informationen.

Hardwarebeschleunigung nutzen

Ein entscheidender Faktor für die Performance von HTML5 ist, dass der Grafikprozessor beim Website-Rendering mitarbeiten kann. Bisher musste die CPU diese Aufgabe allein bewerkstelligen. Jetzt können bestimmte Tasks an die GPU (Graphics Processing Unit) ausgelagert werden, nämlich:

Während die beiden letztgenannten Punkte eher spezielle Webapplikationen betreffen, können die ersten drei helfen, so gut wie jede Anwendung zu beschleunigen.

Beschleunigung: Die CSS3-Eigenschaft transition ermöglicht die Umsetzung von Seitenelementen, die zuvor nur mittels JavaScript realisiert werden konnten. Dabei sorgt sie ganz nebenbei für einen Geschwindigkeitszuwachs.
Foto: Demo von http://webstandard.kulando.de

Voraussetzung für die Übergabe bestimmter Aufgaben an den Grafikprozessor ist deren richtige Definition. Im Prinzip sollten HTML5-Dokumente so gegliedert werden, dass einzelne Beschleunigungsprozesse durch die GPU nicht von anderen behindert werden. Dazu müssen die Dokumente in einzelne Elemente strukturiert werden, die der Browser wie gewohnt rendern kann.

Der Grafikprozessor wird beim Zusammensetzen mehrerer Elemente zur fertigen Website automatisch genutzt, wobei er die "Effekte" berechnet, die er beschleunigen kann. Ein möglicher Nutzen: Ein Objekt kann auf einer Website bewegt werden, ohne dass die ganze Seite neu gerendert werden muss.

Beispiel

Nehmen wir ein einfaches Beispiel: Sie wollen Objekt1 im Browser von links nach rechts bewegen. Der herkömmliche Lösungsansatz wäre, einen JavaScript-Timer zu setzen und dann alle n Millisekunden die CSS-Eigenschaft left neu zuzuweisen:

var elem = document.getElementById('Objekt1'),

left = 0;

function move() {

left += 2;

elem.style.left = left + 'px';

return left < 650; }

elem.addEventListener('click', function loop() {

move() && setTimeout(loop, 1000 / 60); }, false);

Eine sanfte Animation ist aber nicht gewährleistet - der Browser weiß ja auch gar nicht, dass es eine solche sein soll. Anders bei HTML5 und CSS3: Sie definieren einfach die finale Position des Objekts und sagen dem Browser, er soll eine Animation zu dieser Position in der vorgegebenen Zeit durchführen. Wie er das macht, bleibt ihm überlassen.

#transanim {

-moz-transition: all 2s ease-out;

-o-transition: all 2s ease-out;

-webkit-transition: all 2s ease-out;

transition: all 2s ease-out; }

var elem = document.getElementById('Objekt1');

elem.addEventListener('click', function loop() {

elem.style.left = '650px'; }, false);

Wichtig ist immer nur: Lassen Sie den Browser einfach erkennen, für welche Bereiche Ihrer Webapplikation sich der Einsatz der Hardwarebeschleunigung lohnt.

Einfacher CSS-Trick: 3D-Fake

Derzeit nutzen die meisten Browser die Hardwarebeschleunigung nur, wenn sie klar erkennen, dass ein HTML-Element davon profitieren würde.

Das deutlichste Zeichen für sie ist, wenn einem Element eine 3D-Transformation zugewiesen wurde. Nun sagen Sie sicher: Ich will ja gar keine 3D-Transformation! Die brauchen Sie auch nicht wirklich - für einen Performance-Gewinn kann es schon genügen, eine vorzutäuschen:

-webkit-transform: translateZ(0);

Der Browser glaubt, das Element solle dreidimensional dargestellt werden, und schickt es zum Rendern an den Grafikprozessor. Diesen Trick sollten Sie allerdings nur sparsam einsetzen.

Gut anzuwenden ist er bei animierten Objekten, die keine anderen bewegten Objekte enthalten. Und wie aus der CSS-Eigenschaft ersichtlich wird, lassen sich davon auch nur Webkit-Browser beeinflussen.

CSS3-Eigenschaft transition

Die CSS3-Eigenschaft transition macht die Umsetzung von animierten Elementen wie sich öffnenden Menüs oder skalierbaren Kästen für jedermann einfach. Was viele nicht wissen: Durch den Einsatz von transition kann auch die Performance gegenüber vielen herkömmlichen Lösungen, die meist auf JavaScript setzen, klar verbessert werden. Die WebKit-Browser Chrome und Safari (MacOS und iOS) nutzen bei transition die Hardwarebeschleunigung, weitere Browser werden damit wahrscheinlich folgen.

Hilfsmittel: Hinter dem Cross Browser CSS3 Rule Generator verbirgt sich ein Stylesheet, das die fehlende CSS3-Unterstützung in Browsern wie dem Internet Explorer 8 ausmerzt. Es sorgt dafür, dass viele CSS3-Möglichkeiten auf allen aktuellen Browsern nutzbar sind.

Für die Nutzung verschiedener CSS3-Eigenschaften in allen gängigen Browsern gibt es derweil mehrere Lösungen, etwa den Cross Browser CSS3 Rule Generator. Einige bekannte Bibliotheken wie scripty2, YUI transition oder jQuery animate enhanced beinhalten APIs für animierte Seitenelemente, die - wo möglich - transition nutzen, andernfalls eine Standardanimation, basierend auf JavaScript, anbieten.

Hardwarebeschleunigung überwachen

In Google Chrome gibt es ein nützliches Kommandozeilenattribut. Damit zeigt der Browser, wie die Hardwarebeschleunigung genutzt wird: Entsprechende Elemente auf einer Website werden rot umrahmt. Starten Sie Chrome mit dem Attribut:

--show-composited-layer-borders

Auch Safari unter Mac OS lässt sich über das Terminal so starten, dass er alle hardwarebeschleunigte Teile einer Website rot kennzeichnet:

$ CA_COLOR_OPAQUE=1 /Applications/Safari.app/Contents/MacOS/Safari

Optimierungspotenzial erkennen

Sie wissen, dass Ihre Applikation besser performen könnte. Doch wo genau liegt das Optimierungspotenzial? Um das herauszufinden, sollten Sie verschiedene Profiling-Methoden nutzen.

Darüber erhalten Sie Auskunft, welche Teile des Codes von einem Umarbeiten am meisten profitieren könnten. Das ist wichtig, weil unnötige Änderungen am Code meistens einen negativen Einfluss auf die Wartungstauglichkeit haben - der Code wird womöglich unnötig aufgebläht und dadurch unübersichtlich.

Methode 1: JavaScript-Profiling

Die Performance von JavaScript hat einen starken Einfluss darauf, wie flüssig sich eine Webapplikation "anfühlt" und wie reaktionsfreudig sie tatsächlich ist. JavaScript-Profiler geben einen Überblick über die Performance der Applikation auf Skript-Level. Sie messen die Zeit, die jede einzelne Funktion von Anfang bis Ende der Ausführung benötigt. Für Google Chrome ist die Nutzung des Profiles Panel innerhalb der Developer-Tools zu empfehlen. Für Firefox gibt es das bekannte Add-on Firebug. Mit dem Online-Tool jsPerf lassen sich JavaScript-Snippets schnell und einfach auf Performance testen, indem man sie in ein Webformular einfügt.

Bildergalerie:
JavaScript-Profiling
Das Profiles Panel als Bestandteil der Developer Tools für Google Chrome hilft bei der Analyse von JavaScript-Code.
JavaScript-Profiling
Der Profiler ermittelt die Ausführungszeiten für JavaScript-Funktionen
JavaScript-Profiling
Firebug ist ein umfangreiches Analyse-Werkzeug für Firefox. Das Add-on dient unter anderem auch als JavaScript-Profiler, ermittelt also die Geschwindigkeiten für die Ausführung verschiedener JavaScript-Funktionen.
JavaScript-Profiling
Für das Austesten einfacher JavaScripts genügt unter Umständen auch das Online-Tool jsPerf. Skripte werden einfach in die entsprechenden Web-Formulare eingefügt, woraufhin jsPerf eine Performance-Messung durchführt.

Für die richtige Interpretation der Ergebnisse aller JavaScript-Profiler muss man wissen: Während sie die Ausführungszeit der einzelnen Funktionen messen, berechnen die Profiler indirekt auch die Ausführungszeit für DOM-Operationen. Gerade diese Kommandos sind oft der Kern für mögliche Performance-Verbesserungen, wie wir im Folgenden noch sehen werden. Schauen Sie sich zum Beispiel folgenden Code an:

function drawArray(array) {

for(var i = 0; i < array.length; i++) {

document.getElementById('test').innerHTML += array[i];

}

}

Im Prinzip wird hier so gut wie keine Zeit für die Ausführung von JavaScript benötigt. Dennoch wird ein JavaScript-Profiler für die Funktion eine lange Ausführungszeit ermitteln - ganz zu Recht, weil die Funktion mit dem DOM in einer die Performance schwächenden Weise interagiert.

Zwei Tipps für das JavaScript-Profiling

Tipp 1: Anonyme Funktionen sind für JavaScript-Profiler ein Problem. Die Entwickler-Tools benötigen in der Regel einen Funktionsnamen, um Ergebnisse exakt auflisten zu können. Für das Problem gibt es eine elegante Lösung, die mit allen gängigen Browsern kompatibel ist. Schreiben Sie anstelle von

$('.beispiel').each(function() { ... });

einfach

$('.beispiel').each(function FunktionBeispiel() { ... });

Tipp 2: Stellen Sie sich vor, Sie haben eine JavaScript-Funktion mit sehr viel Code. Sie vermuten, dass nur ein kleiner Teil davon der Grund für Performance-Probleme ist. Abgesehen von der schlauesten Methode, den Code vollständig zu überarbeiten und lange Funktionen zu vermeiden, kommen Sie vielleicht auch mit diesem Trick weiter: Fügen Sie Statements in Form sich selbst aufrufender Funktionen in Ihren Code ein. Wenn Sie vorsichtig vorgehen, ändert das nichts an der Semantik Ihres Codes. JavaScript-Profiler können aber jeden Teil des Codes als individuelle Funktion erkennen und ausmessen:

function langeFunktion() {

...

(function einTeilderArbeit() {

...

})();

...

}

Vergessen Sie aber nicht, diese Funktionen nach dem Profiling wieder zu entfernen. Oder noch besser: Überarbeiten Sie anschließend den Code gleich richtig.

Methode 2: DOM-Profiling

Die Google-Chrome-Web-Inspector/Developer-Tools bieten die "Timeline View". Diese Ansicht stellt den zeitlichen Ablauf aller Aktionen dar, die vom Browser ausgeführt werden.

Die dynaTrace Ajax Editions sind eine kostenlose Erweiterung für den Internet Explorer: Sie ermöglichen das DOM-Profiling auf komfortable Art und Weise. Die Website erklärt das Arbeiten dynaTrace genau und bietet den Download-Link an.

Diese Informationen können Sie nutzen, um DOM-Operationen zu optimieren. Ziel sollte sein, die Anzahl der Operationen zu verringern, die der Browser auszuführen hat.

Weil die "Timeline View" schnell sehr unübersichtlich werden kann, bietet es sich an, beim DOM-Profiling einzelne Teile der Applikation getrennt zu betrachten. Mehr Informationen zur "Timeline View" erhalten Sie in dieser Beschreibung der Chrome-Developer-Tools. Ein alternatives Tool für das DOM-Profiling im Internet Explorer ist DynaTrace Ajax Edition.

Strategien für die Performance-Optimierung

Haben Sie erst einmal Potenzial in Sachen Performance erkannt, gibt es verschiedene Wege, Ihre Applikation zu optimieren. Die Umbauarbeiten beginnen in der Regel mit der schon angesprochenen Minimierung der DOM-Interaktionen. Während sich JavaScript-Code an sich gut optimieren lässt, bremsen DOM-Interaktionen Weboapplikationen immer wieder aus.

Wann immer Sie vom DOM Knoten empfangen, sollten Sie darüber nachdenken, ob sie diese nicht später wiederverwenden können. Legen Sie Knoten in einen Cache, von wo aus sie später zurückgeholt werden können. Schreiben Sie anstatt

function getElements() {

return $('.my-class'); }

besser

var cachedElements;

function getElements() {

if (cachedElements) {

return cachedElements;

cachedElements = $('.my-class');

return cachedElements; }

Genauso wie Sie DOM-Knoten in einem Cache ablegen können, machen Sie es auch mit den Werten von Attributen. Ein Beispiel für eine saubere HTML5/JavaScript-Lösung: Sie animieren ein Objekt durch Veränderung der Attributwerte des Knotens. Speichern Sie den letzten Wert jedes Bewegungsschritts, sodass Sie ihn nicht wiederholt auslesen müssen. Anstatt

setInterval(function() {

var ele = $('#Element');

var left = parseInt(ele.css('left'), 10);

ele.css('left', (left + 5) + 'px');

}, 1000 / 30);

schreiben Sie:

var ele = $('#Element');

var left = parseInt(ele.css('left'), 10);

setInterval(function() {

left += 5;

ele.css('left', left + 'px');

}, 1000 / 30);

Vorsicht mit Schleifen

Schleifen sind heiße Kandidaten für die Performance-Optimierung - sie machen fast jede Applikation langsamer. Am besten wird ihr Einsatz deshalb ganz vermieden, wo immer andere Lösungen möglich sind. Lassen Sie lieber eine Berechnung ausführen, warten Sie das Ergebnis ab und übergeben Sie es dann an das DOM, anstatt in einer Schleife mit dem DOM zu interagieren. So kann beispielsweise aus dem Code

document.getElementById('target').innerHTML = '';

for(var i = 0; i < array.length; i++) {

var val = doSomething(array[i]);

document.getElementById('target').innerHTML += val; }

folgender Quelltext werden:

var stringBuilder = [];

for(var i = 0; i < array.length; i++) {

var val = doSomething(array[i]);

stringBuilder.push(val); }

document.getElementById('target').innerHTML = stringBuilder.join('');

Layout-Änderungen und Animationen

Am langsamsten wird der DOM-Zugriff, wenn ein ständiger Wechsel aus Lesen, Berechnen von Attributwerten und Ausgabe im Gange ist. Deshalb sollte Ihr Code idealerweise so strukturiert sein, dass in einer ersten Phase nur Werte gelesen werden. In einer zweiten Phase wird dann das DOM modifiziert. Ersetzen Sie Code wie

function langsameSache() {

var left1 = $('#Objekt1').css('left');

$('#anderesObjekt1').css('left', left);

var left2 =[k] $('#Objekt2').css('left');

$('#anderesObjekt2').css('left', left); }

durch

function schnelleSache () {

var left1 = $('#Objekt1').css('left');

var left2 = $('#Objekt2').css('left');

$('#anderesObjekt1').css('left', left);

$('#anderesObjekt2').css('left', left); }

In der ersten Variante müssen Styles und Layout zweimal neu berechnet werden, in der zweiten nur einmal - für das exakt gleiche Ergebnis. Messen Sie mit einem Profiler nach und überzeugen Sie sich selbst vom Geschwindigkeitsgewinn durch die zweite Version. Eine entsprechende Optimierung lässt sich bestimmt in sehr vielen Fällen anwenden - mit erstaunlichen Ergebnissen. Allerdings müssen Sie bei Layout-Änderungen und Animationen immer bedenken, wie der Browser JavaScript ausführt, und Ihren Code entsprechend gestalten.

Die JavaScript-Ausführung im Browser folgt nämlich einem bestimmten Timing. Normalerweise befindet sich der Browser in einer Art "Stand-by-Modus". Durch ein vom Nutzer ausgelöstes Event, einen Timer-Aufruf oder eine andere Aktion wird der Browser aus diesem Modus aufgeweckt. In der Regel wartet er, bis die Aktion beendet ist, bevor er ein neues Bild der Website zeichnet (es gibt ein paar Ausnahmen, zum Beispiel der Aufruf von Dialog-Boxen). Das hat eine wichtige Konsequenz: Wenn Ihr JavaScript zur Animation eines HTML5-Objekts länger als 1/25 Sekunden braucht, ehe es fertig ausgeführt ist, können Sie keine flüssige Bewegung im Browser darstellen. Denn der Browser wiederholt das Zeichnen des Objekts eben erst nach Ende der Skriptausführung. Damit das menschliche Auge eine Bewegung sicher als fließend sieht, sollte sie aber aus mindestens 25 Bildern pro Sekunde bestehen.

Schnellerer Seitenaufruf

Nutzer verlangen aufwendige Webapplikationen, die möglichst im Nu geladen sind. Ein Widerspruch? Normalerweise schon, gäbe es nicht für viele Websites deutliches Optimierungspotenzial. Vor allem aber kann es gelingen, den Eindruck einer schnellen Website zu erwecken, indem man ein paar psychologische Gesichtspunkte beachtet. Anwender empfinden nämlich bestimmte Geschwindigkeitsprobleme als störender als andere.

Eine schlechte User Experience entsteht sicherlich, wenn die Applikation während eines Mouseover ins Stocken gerät - dafür nimmt es niemand wirklich übel, wenn nach dem Anklicken eines Buttons ein oder zwei Sekunden vergehen, bevor etwas Neues passiert. Die richtige Schlussfolgerung daraus ist: Lassen Sie Initialisierungs-Code für Events so spät wie möglich ausführen - idealerweise erst dann, wenn er wirklich gebraucht wird. Ersetzen Sie beispielsweise

var things = $('.ele > .other * div.className');

$('#button').click(function() { things.show() });

durch:

$('#button').click(function() { $('.ele > .other * div.className').show() });

Fazit

Moderne Webapplikationen auf Basis von HTML5, CSS3 und JavaScript bieten ein großes Optimierungspotenzial in Sachen Performance. Dabei spielt vor allem die richtige Ausgabe von (Layout-)Grafiken und die Animation von (grafischen) Elementen eine wichtige Rolle.

In der Praxis geht es genau um die in diesem Artikel dargestellten Bereiche: die geschickte Ausnutzung der Hardwarebeschleunigung, die Analyse von Schwachstellen mittels Profiling sowie die daraus hergeleitete Performance-Optimierung. Wie gezeigt, stützt sich Letztere vor allem auf verbesserten JavaScript-Code. Im zweiten Teil dieser Artikelserie werden wir uns mit dem Thema Sicherheit im Zusammenspiel mit HTML5 beschäftigen. (mje)

Der Artikel stammt von der CW-Schwesterpublikation TecChannel. (sh)

Mehr zu HTML5? Lesen Sie auch in unserer HTML5-Serie: