Mehr Performance für dein Webprojekt: Erste Schritte mit RequireJS
JavaScript-Dateien werden normalerweise über das script
-Element in die Webseite eingebunden. Bei kleineren Projekten stellt das absolut kein Problem dar. Wächst aber das Projekt und steigt dabei die Komplexität, verliert man schnell den Überblick – und auch die Performance leidet zunehmend durch überflüssig geladene Skript-Bestandteile.
RequireJS ist ein JavaScript-Datei- und Modul-Loader, der mit Hilfe eines modularen Aufbaus die Geschwindigkeit und Qualität deines Codes verbessern kann. Durch eine strikte Aufteilung der Skripte und eine optimierte Laderoutine gewinnt man so an Struktur und Performance. Wir verraten dir, wie RequireJS funktioniert und wie du deine ersten Schritte mit der Bibliothek machen kannst.
So funktioniert Require.js
RequireJS ist eine Bibliothek, die mit einer Asynchronen-Modul-Definition-API (AMD) ausgestattet, Code-Blöcke und deren Abhängigkeiten für dein Projekt laden und zur Verfügung stellen kann. Das bedeutet, dass du lediglich eine Datei in dein Dokument einbinden musst – egal wie viele Bibliotheken und Skripte du benötigst. Das Laden der benötigten Komponenten übernimmt dann RequireJS für dich.
Hierfür benötigt RequireJS eine feste Struktur, um das automatische Laden von Skripten zu ermöglichen. Für unser Beispiel werden wir exemplarisch ein Hilfs-Skript benutzen, um mit jQuery eine einfache DOM-Manipulation durchzuführen. Dabei werden wir auf folgenden Aufbau zurückgreifen:
- deinProjekt
- projekt.html
- /scripts
- main.js
- require.js
- /libs
- /helper
- util.js
Im Projektordner befindet sich eine projekt.html
-Datei, in der wir JavaScript ausführen wollen. Im Unterordner „/scripts“ befinden sich alle Skripte des Projekts. Dort hinterlegen wir die require.js
-Datei und die main.js
-Datei, mit der wir später die Steuerung übernehmen. Im Unterordner „/libs“ speichern wir verschiedene Bibliotheken und im Unterordner „/helper“ verschiedene Hilfs-Skripte, die wir zur Ausführung benötigen werden. Um RequireJS nutzen zu können, binden wir die Datei jetzt in unser Projekt ein. Hierfür reicht die folgende Code-Zeile aus:
<script data-main="scripts/main" src="scripts/require.js"></script>
Mit dem data-main
-Attribut im script
-Element teilen wir RequireJS mit, welche Datei zur Steuerung der Programmlogik benötigt wird. In unserem Fall ist es die main.js
-Datei. Das Weglassen von „.js“ im data-main
-Attribut ist hierbei keineswegs ein Fehler: RequireJS geht standardmäßig davon aus, dass alle benötigten Dateien die Endung „.js“ haben.
Entwicklung mit RequireJS
Innerhalb der main.js
-Datei können wir jetzt die benötigten Bibliotheken und Skripte laden und ausführen. Hierfür definieren wir in der main.js
-Datei zunächst den Pfad zur jQuery-Bibliothek. Dafür steht uns die requirejs.config()
-Methode zur Verfügung.
requirejs.config({
paths: {
"jquery": [
"https://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min",
// If the CDN fails, load from this local module instead
"lib/jquery"
]
}
});
An die Methode übergeben wir ein Array mit den Pfaden zu den Bibliotheken, die wir verwenden wollen. Dabei definieren wir den Namen der Bibliothek, über den wir später innerhalb der Programm-Logik auf die Bibliothek zugreifen können. Nutzen wir hierbei ein Array, können wir Fallback-Möglichkeiten dafür definieren. In unserem Beispiel verweisen wir zunächst auf ein Content-Delivery-Network (CDN) und geben im Falle eines fehlerhaften Aufrufs den Pfad zur lokalen Kopie der jQuery-Bibliothek an. Mit Hilfe der require()
-Methode hättest du jetzt die Möglichkeit, innerhalb der main.js
-Datei auf die jQuery-Bibliothek zuzugreifen.
require(["jquery"], function($) {
// Dein jQuery Code
});
In unserem Beispiel wollen wir die Arbeit aber auf ein Sub-Skript auslagern. Dazu legen wir im Unterordner „/helper“ die util.js
-Datei an, ein sogenanntes Modul, das zunächst für den Gebrauch mit RequireJS definiert werden muss. Das können wir mit der define()
-Methode erledigen und dabei gleichzeitig die Abhängigkeit von jQuery festlegen. Die Methode folgt dem nachfolgenden Muster:
define(
moduleName, // optional, defaults to name of file
dependencies, // optional array listing this file's dependencies
function(params) {
// Function to execute once dependencies have been loaded
// params contains return values from the dependencies
}
);
In unserem Beispiel könnte die Definition des Moduls folgendermaßen lauten:
define(["jquery"], function($) {
$('body').append("<h2>Dynamischer Inhalt auf util.js</h2>");
}
);
Um nun die util.js
-Datei zu laden, kehren wir zur main.js
-Datei zurück und fordern mit der require()
-Methode das gerade definierte Modul an.
require(["helper/util"]);
Mit dem obenstehenden Code laden wir die Datei util.js
aus dem Unterodner „/helper“. Gleichzeitig lädt RequireJS alle Bibliotheken und Skripte, die zur Ausführung von util.js
benötigt werden – in unserem Fall ist das die jQuery-Bibliothek.
Basis-Implementation und Performance-Optimierung mit RequireJS
Unsere Basis-Implementation von RequireJS is abgeschlossen. Betrachten wir jetzt den Ladevorgang unserer Seite, so sehen wir, dass zunächst require.js
und anschließend die weiteren Skripte des Projektes geladen werden – in der den Abhängigkeiten entsprechenden Reihenfolge.
Bei genauerer Betrachtung kannst du sehen, dass die zusätzlichen Skripte erst nach dem Auslösen des DOM-Events geladen werden. Das heißt, dass die zusätzlich benötigten Skripte den Render-Prozess unserer Seite nicht unnötig hinauszögern und so mit Hilfe von RequireJS neben einer Modularisierung und damit besseren Übersicht auch ein positiver Effekt auf die Performance erzielt werden kann. So können wir sicherstellen, dass auch bei größeren Projekten nur die Dateien übertragen werden müssen, die auch tatsächlich zur Ausführung benötigt werden.
Damit du dieses Experiment nicht nachbauen musst, haben wir dir das RequireJS-Beispielprojekt zum Download hinterlegt.
Mit RequireJS die Performance optimieren
Mit der Technik hinter RequireJS profitiert dein Projekt von einer besseren Übersicht und erhält eine automatische Performance-Optimierung. Mit dem RequireJS-Optimizer kannst du diesen Effekt bei größeren Projekten noch besser ausnutzen: Der RequireJS-Optimizer kombiniert verwandte Skripte, minifiziert sie und führt weitere Optimierungsschritte durch, um eine noch bessere Performance zu erzielen. Der Optimizer wird dann in der Regel beim Feinschliff beziehungsweise Refactoring des Projektes mit Tools wie Grunt eingesetzt.
Mehr Informationen zum RequireJS-Optimizer findest du in der Dokumentation des Skriptloaders. Weitere Informationen und Beispiele sind auch auf der offiziellen Webseite von RequireJS zu finden. Mehr zum Thema Performance findest du in unserer Performance Serie.
Setzt ihr auf Script-Loader bei euren Projekten?
Hallo Ilja,
kennst du zufällig auch ein Plugin, welches es mir ermöglicht, Require.JS in WordPress einzubinden? Oder lohnt sich da eher die Einbindung direkt in das Theme?
Hallo Rico,
WordPress übernimmt das Management der Skripte ja bereits selbst und bietet viele Möglichkeiten um das Verhalten zu steuern. Auch das Definieren von Abhängigkeiten ist möglich. Schau dich mal in der Dokumentation um, da werden die Basics sehr gut erläutert:
http://codex.wordpress.org/Function_Reference/wp_register_script
Hallo Ilja,
mich stört es, dass z.b. durch den Einsatz von verschiedenen Plugins u.a. 10x jquery.js Dateien aufgerufen werden. Manche Entwickler bieten zwar an, den Aufruf abzuschalten wenn man schon manuell eine jquery.js eingebunden hat aber leider fehlt diese Funktion bei den meisten Plugins die ich nutze.
Gibt es hier auch eine Möglichkeit dieses zu unterbinden und nur die manuelle jquery.js laden zu lassen und die anderen zu „blockieren“ ohne im Plugin-Ordner anpassungen vornehmen zu müssen, die nach einem Update dann wieder weg sind?
Vielen Dank!
Hallo Dennis,
WordPress bietet ja mit der Funktion enqueue-scrtipts eine Möglichkeit Skripte in die jeweilige Seite zu laden. Je nachdem wie gut oder schlecht das Ganze vom Plug-in-Entwickler gelöst ist, werden Skripte, die mehrfach benutzt werden nicht doppelt geladen. jQuery ist ein gutes Beispiel.
http://codex.wordpress.org/Function_Reference/wp_enqueue_script
Die Funktion wp_enque_script erwartet als einen der Parameter den Namen des Skripts. jQuery ist bei WordPress mit an Bord. Wenn ein Entwickler sich dazu entschließt eine eigene Version von jQuery zu laden, könnte er es beispielsweise über wp_register_script (http://codex.wordpress.org/Function_Reference/wp_register_script) tun. Dann wird jQuery ggf. noch mal hinzugeladen. Oder noch schlimmer: er nutzt gar nicht die WordPress Hooks und lädt das Skript anders.
Somit gibt es keine wirklich gute Möglichkeit solche Probleme anzugehen. Wenn der Author deiner Plug-Ins unsauber arbeitet, wirst du nicht viel daran ändern können ohne zu riskieren, dass beim Update die ganze Arbeit für die Katz war.
Bei Themes hast du wenigstens die Möglichkeit ein Child-Theme zu erstellen. Child Plug-Ins sind derzeit – soweit ich weiß – nur eine Idee, aber keine Realität.
Du solltest deine jquery.js oder andere Skripte und/oder Stylesheets aber auch nicht manuell einbinden. Dafür gibt es die oben beschriebenen Methoden. (Und für CSS vielleicht das noch: http://codex.wordpress.org/Function_Reference/wp_enqueue_style)
In diesem Beispiel geht es ja um require.js. Das ist mehr was für eigene Projekte und WebApps als für WordPress.
Viele Grüße
Ilja
Hallo Ilja,
vielen Dank für diesen ausführlichen Kommentar! Ich werde mir das ganze mal genauer anschauen.
Nochmals vielen Dank und ein schönes Wochenende wünsche ich dir!
Viele Grüße,
Dennis