Webkrauts Logo

Webkrauts Webkrauts Schriftzug

- für mehr Qualität im Web

Große JavaScript Applikationen leicht gemacht

Wir haben noch nicht alle älteren Artikel nachbearbeitet. Das bezieht sich in der Regel nur auf die Codebeispiele, die noch nicht optimal dargestellt werden.

Große JavaScript Applikationen leicht gemacht

Unser heutiger Beitrag ist eine Kooperation mit den Kollegen von 24ways.org. Christian Heilmann erklärt hier und dort, wie man große JavaScript-Applikationen am Geschicktesten vorbereitet und umsetzt.

Heutzutage verwenden wir JavaScript ziemlich anders als früher. Mit dem Aufkommen von Ajax und dem Hype um Web-Applikationen und Social Networks werden unsere JavaScripte immer größer und komplizierter. Da ist es nicht leicht, den Überblick zu behalten. Das wird vor allem problematisch wenn man die Applikation in einem einzigen, großen JavaScript erstellt. Persönlich bin ich sehr dafür, Applikationen in kleine, logische Bausteine aufzuteilen und diese dann mit der richtigen Logik bei Bedarf zusammenzusetzen. Das erlaubt das parallele Arbeiten mehrerer Entwickler am gleichen Projekt und führt ausserdem zu kleineren Applikationen, da Module per Bedarf nachgeladen werden. Hier erkläre ich die ersten Schritte, wie man eine solche Applikation erstellen kann.

Am Anfang steht das Hauptobjekt

Am allerwichtigsten ist es, den JavaScript Code durch „Namespacing“ vor dem Überschreiben durch andere Skripte zu bewahren. Das geht am einfachsten indem man den gesammten Code in einem globalen Objekt speichert, zum Beispiel meineTolleApplikation:

var meineTolleApplikation = {};

Alle Komponenten der Applikation werden Attribute dieses Objektes und man kann auch gleich von Anfang an testen ob DOM Scripting unterstützt wird oder Sprachdateien laden und so weiter.

Die Definition der Komponenten

Der nächste Schritt ist dem Hauptobjekt ein Komponentenobjekt hinzuzufügen. Darin definiert man die einzelnen Komponenten mit Namen, URL und einem Boolean der angibt ob die Komponente geladen ist oder nicht.

var meineTolleApplikation = {
    komponenten :{
        formular:{
            url:'formular.js',
            geladen:false
        },
        navigation:{
            url:'navigation.js',
            geladen:false
        },
        attachment:{
            url:'attachment.js',
            geladen:false
        },
        lightbox:{
            url:'lightbox.js',
            geladen:false
        }
    }
};

Technisch gesehen kann man das "geladen" Attribut auch weglassen, aber so ist es sauberer. Als nächsten Schritt ist es sinnvoll eine neueKomponente Methode anzufügen, die es erlaubt, dynamisch Komponenten zur Applikation hinzuzufügen. Das wird am einfachsten dadurch erreicht, indem man neue SCRIPT Elemente dem Dokumentenkopf hinzufügt.

var meineTolleApplikation = {
    komponenten :{
        formular:{
            url:'formular.js',
            geladen:false
        },
        navigation:{
            url:'navigation.js',
            geladen:false
        },
        attachment:{
            url:'galerie.js',
            geladen:false
        },
        lightbox:{
            url:'lightbox.js',
            geladen:false
        }
    },
    neueKomponente:function(komponente){
        var c = this.komponenten[komponente];
        if(c && c.geladen === false){
            var s = document.createElement('script');
            s.setAttribute('type', 'text/javascript');
            s.setAttribute('src',c.url);
            document.getElementsByTagName('head')[0].appendChild(s);
        }
    }
};

Dadurch kann man neue Komponenten der Applikation hinzufügen wenn diese noch nicht geladen sind:

if(!meineTolleApplikation.komponenten.galerie.geladen){
    meineTolleApplikation.neueKomponente('galerie');
};

Sind die Komponenten wirklich geladen?

Das ist allerdings nicht wirklich sicher, da wir nicht wissen ob die Skripte wirklich geladen wurden. Um das einwandfrei sicherzustellen ist das einfachste, jeder Komponente eine Zeile hinzuzufügen, die dem Hauptobjekt meldet, das sie geladen ist:

var meineTolleApplikation = {
    komponenten :{
        formular:{
            url:'formular.js',
            geladen:false
        },
        navigation:{
            url:'navigation.js',
            geladen:false
        },
        galerie:{
            url:'galerie.js',
            geladen:false
        },
        lightbox:{
            url:'lightbox.js',
            geladen:false
        }
    },
    neueKomponente:function(komponente){
        var c = this.komponenten[komponente];
        if(c && c.geladen === false){
            var s = document.createElement('script');
            s.setAttribute('type', 'text/javascript');
            s.setAttribute('src',c.url);
            document.getElementsByTagName('head')[0].appendChild(s);
        }
    },
    komponenteVerfuegbar:function(komponente){
        this.komponenten[komponente].geladen = true;
    }
}

Zum Beispiel sollte das galerie.js Skript sich als letzte Zeile beim Hauptobjekt anmelden:

meineTolleApplikation.galerie = function(){
    // [... other code ...]
}();

meineTolleApplikation.komponenteVerfuegbar('galerie');

Und wie teilen wir es den Anwendern mit das eine Komponente vorhanden ist?

Der letze Schritt ist eigentlich nur eine nette Maßnahme gegenüber den Entwicklern der Applikation. Wir fügen den Aufruf einer "lauscher" Methode hinzu, die den Namen einer jeden Komponente erhält, die geladen wurde:

var meineTolleApplikation = {
    komponenten :{
        formular:{
            url:'formular.js',
            geladen:false
        },
        navigation:{
            url:'navigation.js',
            geladen:false
        },
        galerie:{
            url:'galerie.js',
            geladen:false
        },
        lightbox:{
            url:'lightbox.js',
            geladen:false
        }
    },
    neueKomponente:function(komponente){
        var c = this.komponenten[komponente];
        if(c && c.geladen === false){
            var s = document.createElement('script');
            s.setAttribute('type', 'text/javascript');
            s.setAttribute('src',c.url);
            document.getElementsByTagName('head')[0].appendChild(s);
        }
    },
    komponenteVerfuegbar:function(komponente){
        this.komponenten[komponente].geladen = true;
        if(this.lauscher){
            this.lauscher(komponente);
        };
    }
};

Das erlaubt es Entwicklern die lauscher Methode zu schreiben, die darauf wartet das Komponenten geladen werden und dann auf diese reagiert:

meineTolleApplikation.lauscher = function(komponente){
    if(komponente === 'galerie'){
       zeigegalerie();
    }
};

Eigene Komponenten hinzufügen

Da das Hauptobjekt veränderbar ist können Entwickler das komponenten Objekt mit eigenen Komponenten erweitern und innerhalb einer lauscher Funktion andere abhängige Komponenten nachladen. Sagen wir zum Beispiel wir haben eine neuekomponente mit Daten und Beschreibungen in eigenen Dateien:

meineTolleApplikation.lauscher = function(komponente){
    if(komponente === 'neuekomponente'){
        meineTolleApplikation.neueKomponente('beschreibungen');
    };
    if(komponente === 'beschreibungen'){
        meineTolleApplikation.neueKomponente('daten');
    };
    if(komponente === 'daten'){
        meineTolleApplikation.neuekomponente.init();
    };
};
meineTolleApplikation.komponenten.neuekomponente = {
    url:'neue.js',
    geladen:false
};
meineTolleApplikation.komponenten.beschreibungen = {
    url:'beschreibungen.js',
    geladen:false
};
meineTolleApplikation.komponenten.daten = {
    url:'daten.js',
    geladen:false
};
meineTolleApplikation.neueKomponente('neuekomponente');

Wenn man seine Applikation auf diese Art und Weise plant hat man totale Kontrolle über den Status der einzelnen Komponenten und kann sogar CSS bei Bedarf nachladen.

Woher kommt diese Idee?

Falls Sie diese Idee mögen und sich fragen ob das auch schon mal in einer echten Applikation angewandt wurde möchte ich auf die Yahoo! User Interface Library weisen, im Speziellen die YAHOO_config Option und das globale YAHOO.js Objekt.

Anmerkung der Redaktion

24 ways to impress your friendsDieser Artikel ist die deutsche Übersetzung von Christians Beitrag für die Seite 24ways. Dieser Adventskalender bietet noch viele weitere interessante Beiträge, alle in englischer Sprache.

Kommentare

Webstandard-Team
am 18.12.2007 - 08:30

Der beste Artikel in der Serie des diesjährigen Adventskalenders. Sehr anschaulich beschrieben und kann somit leicht nachvollzogen werden.

Permanenter Link

Christian Knothe
am 18.12.2007 - 08:53

Wer sich weitergehend mit dem Thema beschäftigen möchte, dem sei auch die "Bibel" zum Thema JavaScript ans Herz gelegt: JavaScript. Das umfassende Referenzwerk von David Flanagan. Darin gibt es ein eigenes Kapitel zum Thema Modules und Namensräume. Sehr lesenswert!

Permanenter Link

Carsten Witt
am 18.12.2007 - 10:11

Vielen Dank, Christian, für diesen famosen Beitrag!
Schöne, streßfreie Weihnachten :-)

Permanenter Link

Daniel
am 18.12.2007 - 10:18

Galerie mit 2 "L"...? Könnte man vielleicht noch korrigieren oben im Script. ;-)

Permanenter Link

David
am 18.12.2007 - 12:43

Super Beitrag, danke!

Permanenter Link

nikosch
am 18.12.2007 - 14:55

Die Ähnlichkeit zum 18. Kalendertürchen von 24ways fällt doch arg auf. Ein bißchen einfallsreicher wäre es gegangen, auch wenn der Inhalt natürlich sehr gut ist...

http://24ways.org/2007/keeping-javascript-dependencies-at-bay

Permanenter Link

nikosch
am 18.12.2007 - 14:57

Schande über mich. Man sollte die Artikel doch bis zum Ende lesen. Sorry.

Permanenter Link

Die Kommentare sind geschlossen.