Donnerstag, 25. August 2011

JavaScript richtig integrieren

Eine Webapplikation ohne den Einsatz von JavaScript, ist mittlerweile kaum noch vorstellbar. JavaScript wird unter anderem für das Nachladen und Ersetzen von DOM-Elementen, sowie für Event-Verarbeitung und Animationen genutzt.

Es gibt unterschiedliche Möglichkeiten JavaScript zu integrieren, wobei sich einige im Bezug auf die resultierende Performance der Website besser eignen als andere. Dieser Artikel fasst einige Empfehlungen aus dem Buch High Performance Web Sites von Steve Souders zusammen und soll darüber hinaus noch Tipps für die JavaScript-Entwicklung geben.

Externe JavaScript-Dateien

Für die Einbindung von JavaScript gibt es zwei Wege. Zum einen direkt in der HTML-Datei innerhalb eines Script-Blocks und zum anderen in einer externen JavaScript-Datei referenziert durch ein Script-Tag. Da bei der Lösung mit externer Datei ein zusätzlicher HTTP-Request durchgeführt werden muss, lädt die Website mit integriertem Script-Block beim ersten Aufruf schneller. Der große Nachteil an Script-Blöcken ist jedoch, dass diese bei jedem Request mit übertragen werden müssen. Externe JavaScript-Dateien können hingegen vom Browser gecached werden.

Eine minifizierte JavaScript-Datei

Zusätzlich sollte beachtet werden, dass nur eine JavaScript-Datei eingebunden wird, da jede Datei durch einen eigenen Request geladen werden muss. Das Skript sollte außerdem minifiziert vorliegen, um die zu übertragenen Bytes zu minimieren. Gute Ergebnisse können beispielsweise mit UglifyJS, Google Closure Compiler oder YUI Compressor erreicht werden.

JavaScript ans Ende und erst nach dem Rendern ausführen

Die Browser rendern eine Seite von oben nach unten und laden dabei alle benötigten externen Ressourcen wie Stylesheets, Bilder und JavaScripts. Da nur eine begrenzte Anzahl an Requests parallel durchgeführt werden kann (RFC2616 empfiehlt zwei parallele Requests pro Domain, moderne Browser unterstützen jedoch mehr), sollten Scripts erst am Ende der Seite eingebettet werden, damit das initiale Rendern von sichtbaren Elementen nicht gebremst wird.

Außerdem werden JavaScripts direkt nach dem Laden evaluiert, wodurch der Rendervorgang pausiert wird. Daher empfiehlt es sich, die Ausführung des JavaScripts an ein Browser-Event zu binden, welches erst nach dem vollständigen Rendern ausgelöst wird. Im folgenden Beispiel vereinheitlicht jQuery die Events der unterschiedlichen Browser und ruft den Event-Handler auf, nachdem die Seite vollständig geladen wurde:

$(document).ready(function() {

// do stuff

});


In diesem Abschnitt wird ein eine anonyme Funktion als Event-Handler an das Ready-Event des HTML-Dokuments gebunden. Innerhalb dieser Funktion sollten alle weiteren JavaScript Funktionen gekapselt werden.

Entwicklungs- und Produktionsmodus

Eine einzelne, und obendrein noch minifizierte JavaScript-Datei, ist selbstverständlich durch keinen Entwickler wartbar und im Fehlerfall kann die Zeilennummer in der Browserkonsole keinen Aufschluss über die Fehlerquelle geben. Daher wird ein Mechanismus benötigt, welcher dafür Sorge trägt, dass im Produktionsmodus die minifizierte und im Entwicklungsmodus die normale JavaScript-Datei eingebunden werden. Die Zusammenfassung der einzelnen JavaScript-Dateien und die anschließende Minifizierung erfolgen dabei in einem Postprozess während des Deployments.

Je nach Servertechnologie und Template-Engine, könnte dies wie folgt aussehen: Eine Variable, deren Wert in der Serverkonfiguration oder durch eine Umgebungsvariable definiert wird, bestimmt, ob die basic.js oder die basic-min.js eingebunden wird.

<% if env === "development" %>

<script type="text/javascript"

src="/extensions/js/basic.js"></script>

<% else %>

<script type="text/javascript"

src="/extensions/js/basic-min.js"></script>

<% end %>


Dadurch wird im Entwicklungsmodus die nicht-minifizierte basic.js eingebunden. Somit hat der Entwickler bessere Möglichkeiten für das Debuggen des JavaScripts.

Aufteilung in mehrere Module

In einem Projekt, in dem viel JavaScript eingesetzt wird, wird eine Datei sehr schnell unübersichtlich und unwartbar. Erschwerend kommt hinzu, dass oftmals mehrere Personen an den Entwicklungsarbeiten beteiligt sind, wodurch Konflikte entstehen können.

Aus diesem Grund ist es sinnvoll, das gesamte JavaScript in mehrere Dateien zu modularisieren. Templateänderungen führen bei einigen Systemen dazu, dass HTML-Seiten neu publiziert werden müssen. Daher sollte das Hinzufügen von neuen JavaScript-Modulen nicht durch ergänzen von Script-Tags im Template erfolgen. Vielmehr sollte es möglich sein, dass Anpassungen zentral in einer JavaScript-Datei vorgenommen werden können.

Dies gelingt, indem die einzelnen Script-Tags für die Module durch ein JavaScript (basic.js) in die Seite geschrieben werden:

(function() {

var files = [

"/extensions/js/file1.js",

"/extensions/js/file2.js"

];


(function(scripts) {

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

scripts[i] = "<script type=\"text/javascript\"" +

"src=\"" + scripts[i] + "\"></script>";

}

document.write(scripts.join("\n"));

})(files);

})();


Dieses JavaScript iteriert über eine Liste der angegebenen URIs der JavaScript-Dateien und erstellt für jede ein Script-Tag. Diese werden anschließend gemeinsam in das HTML-Dokument geschrieben, bevor der Browser das Rendern des Dokuments beendet hat. Soll ein Modul hinzugefügt oder entfernt werden, muss lediglich die Liste files angepasst werden.

Ausliefern der JavaScript-Dateien

Einige Websites liefern JavaScript-Dateien wie andere statische Ressourcen (z. B. Bilder) mit einem Content Management System aus. Dies ist jedoch mit Sicherheit nicht die beste Möglichkeit, da JavaScript keinen Seiteninhalt, sondern vielmehr einen Bestandteil der Software darstellt. Außerdem müsste für jede Anpassung des JavaScripts jeweils eine Publikation angestoßen werden. Stattdessen sollte JavaScript direkt von einem Webserver ausgeliefert werden. nginx und andere Webserver bieten für diesen Fall den Einsatz eines Rewrites an, wodurch ein Pfadmuster gesondert behandelt werden kann. Anfragen, welche mit http://meineseite.de/extensions/js/ beginnen werden abgefangen und können mit Ressourcen unterhalb von /var/www/meineseite.de/extensions/js beantwortet werden. Alle anderen Requests werden wie gewohnt vom verwendeten System beantwortet.

server {

server_name meineseite.de;

listen 80;


location / {

rewrite /extensions/js/(.*)

/var/www/meineseite.de/extensions/js/$1;

}

}


Kleiner Aufwand für große Auswirkungen

Die in diesem Artikel beschriebenen Tipps können zum größten Teil mit wenig Mühe umgesetzt werden. Dennoch können diese erheblich auf die Performance Ihrer Website einzahlen. Weiterhin kann dadurch auch der Komfort für die Entwicklungsarbeiten erhöht werden.

Keine Kommentare:

Kommentar veröffentlichen