Model View Controller mit Backbone.js

Backbone.js ist das mit Abstand am weitesten verbreitete MVC-Framework für die Entwicklung von Single-Page-Anwendungen – doch ist es auch das Beste? Um diese Frage beantworten zu können, muss man zunächst die Konzepte von Backbone.js verstehen und einordnen.

In Pocket speichern vorlesen Druckansicht 1 Kommentar lesen
Lesezeit: 22 Min.
Von
  • Golo Roden
Inhaltsverzeichnis

Backbone.js ist das mit Abstand am weitesten verbreitete MVC-Framework für die Entwicklung von Single-Page-Anwendungen – doch ist es auch das Beste? Um diese Frage beantworten zu können, muss man zunächst die Konzepte von Backbone.js verstehen und einordnen.

Anders als Knockout.js verwendet Backbone.js nicht das moderne Entwurfsmuster Model View ViewModel (MVVM), sondern das deutlich ältere und daher weiter verbreitete Model View Controller (MVC). Allerdings passt es einige Aspekte von MVC an seine individuellen Anforderungen an, setzt MVC also nicht in Reinform um.

Die ursprüngliche Idee des MVC-Entwurfsmusters war, einer Anwendung eine Komponente hinzuzufügen, die zwischen der Darstellung der Daten und der fachlichen Domäne vermittelt. Auf diesem Weg kann man Eingaben gebündelt verarbeiten und im Anschluss alle relevanten Ansichten aktualisieren, ohne die hierfür erforderliche Logik replizieren oder verteilen zu müssen.

Den Kern von Backbone.js bildet die Klasse Backbone.Events, von der sich wiederum Klassen wie Backbone.Model und Backbone.Collection ableiten. Da sie das Beobachtermuster implementiert, ermöglicht Backbone.Events eine entkoppelte und zugleich standardisierte Kommunikation innerhalb einer Backbone.js-Anwendung.

Für die Vererbung greift Backbone.js auf die Bibliothek Underscore.js zurück, die für zahlreiche Webentwickler ohnehin zur Standardausstattung gehört. Dass die Wahl ausgerechnet auf sie gefallen ist, ist kein Zufall: Underscore.js stammt vom gleichen Autor wie Backbone.js und ist aus diesem hervorgegangen.

Um eine Domäne zu modellieren, kennt Backbone.js die beiden bereits angesprochenen Klassen Backbone.Model und Backbone.Collection: Während erstere dazu dient, einzelne Entitäten zu beschreiben, stellt letztere eine Liste dieser dar. Die beiden Klassen bilden also eine 1:n-Beziehung.

Eine Entität kann in Backbone.js nicht nur Daten, sondern auch Geschäftsfunktionen enthalten; gleiches gilt für eine Entitätsliste. Referenzen zwischen Entitäten unterstützt Backbone.js dabei allerdings nicht: Es bleibt dem Entwickler überlassen, Beziehungen entweder über das manuelle Verwalten von IDs oder das Verwenden von Aggregation zu modellieren.

Backbone.js enthält außerdem die Klasse Backbone.View, die der Anzeige von Entitäten und Listen dient. Allerdings kapselt eine View lediglich den Zugriff auf den Webbrowser, die eigentliche Aktualisierung der grafischen Oberfläche führt Backbone.js nicht durch. Hierfür muss der Entwickler auf eine Bibliothek wie jQuery zurückgreifen.

Um Backbone.js zu verwenden, ist zunächst die passende Skriptdatei von der Webseite der Bibliothek herunterzuladen und einzubinden. Da Backbone.js unter anderem für die Vererbung auf Underscore.js zurückgreift, gilt für sie das Gleiche. Darüber hinaus empfiehlt die Dokumentation von Backbone.js den Einsatz von JSON3 und jQuery:

<!doctype html>
<html>
<head>
<title>Backbone.js-Demo</title>
</head>
<body>
<script type='text/javascript' src='json3.min.js'></script>
<script type='text/javascript' src='jquery-2.0.3.min.js'></script>
<script type='text/javascript' src='underscore-min.js'></script>
<script type='text/javascript' src='backbone.js'></script>
</body>
</html>

Anschließend kann man die beiden Klassen Backbone.Model und Backbone.Collection verwenden, um die Domäne mithilfe von Entitäten und Listen zu modellieren. Ausgangspunkt dafür ist stets das Ableiten von einer der beiden Klassen, wobei man die Modellierung in der Regel mit einer Entität beginnt:

var Customer = Backbone.Model.extend({
defaults: {
firstName: '',
lastName: ''
}
});

Die defaults-Eigenschaft definiert die Standardwerte einer neuen Entität. Customer stellt allerdings noch keine solche dar, sondern lediglich deren Blaupause. Um einen konkrete Entität zu erzeugen, muss man Customer als Konstruktor aufrufen. Dabei kann man die Werte der Entität als Parameterobjekt übergeben:

var customer = new Customer({
firstName: 'Golo',
lastName: 'Roden',
age: 34
});

Um auf die Werte zu einem späteren Zeitpunkt wieder zugreifen zu können, stellt Backbone.js die beiden Funktionen get und set zur Verfügung:

console.log(customer.get('firstName')); // => 'Golo'
customer.set('age', customer.get('age') + 1);

Möchte man mehrere Werte gleichzeitig setzen, lässt sich der set-Funktion ein Parameterobjekt übergeben, ähnlich wie beim Aufruf des Konstruktors.

Unabhängig davon, wie die neuen Werte gesetzt werden, benachrichtigt die Entität die Außenwelt über ihre Veränderung: Zum einen löst sie ein allgemeines change-Ereignis aus, zum anderen ein spezielles change:*-Ereignis für jeden einzelnen geänderten Wert. Da Backbone.js jeder Entität über die Basisklasse Backbone.Events eine on-Funktion zur Verfügung stellt, ist es ein Leichtes, auf diese Ereignisse zu reagieren:

customer.on('change:lastName', function (model, value, options) {
// ...
});

Weist man einer Entität einen Wert mit der Bezeichnung id zu, behandelt Backbone.js sie auf besondere Art: Jede Entität verfügt über eine direkte Eigenschaft mit diesem Namen, auf die sich ohne den Umweg über die get-Funktion zugreifen lässt. Außerdem verwendet Backbone.js diese Eigenschaft intern, um eine Entität eindeutig zu identifizieren, beispielsweise innerhalb einer Liste.

Gelegentlich benötigt man statt einer Entität ein reines Datenobjekt. Da Backbone.js mit Ausnahme der Eigenschaft id alle Werte intern kapselt, lässt sich mit der Funktion toJSON eine Entität in ein solches Objekt umwandeln:

var pureData = customer.toJSON();
// => {
// firstName: 'Golo',
// lastName: 'Roden',
// age: 35
// }

Listen von Entitäten funktionieren prinzipiell auf die gleiche Art, man verwendet lediglich die Basisklasse Backbone.Collection statt Backbone.Model. Als Konvention für die Bezeichner von Listen hat sich eingebürgert, ihnen das Suffix List anzuhängen. Als Parameter ist einer Liste die zu verwendende Entität zu übergeben:

var CustomerList = Backbone.Collection.extend({
model: Customer
});

Auch in diesem Fall erhält man als Ergebnis keine konkrete Liste, sondern lediglich eine Blaupause, weshalb auch hier CustomerList als Konstruktor aufzurufen ist. Als Parameter kann man dem Konstruktor eine Liste von Entitäten übergeben, mit denen die Liste initialisiert wird:

var golo = new Customer({ firstName: 'Golo', lastName: 'Roden', age: 34 }),
bill = new Customer({ firstName: 'Bill', lastName: 'Gates', age: 57 });
var customers = new CustomerList([ golo, bill ]);

Analog zu der set-Funktion einer Entität stellt Backbone.js eine gleichnamige Funktion für Listen bereit: Bisher nicht enthaltene Entitäten werden der Liste hinzugefügt, bereits enthaltene werden angepasst – oder entfernt, wenn sie der set-Funktion nicht mehr übergeben werden.

Für einen gezielten Zugriff auf einzelne Listeneinträge bietet Backbone.js darüber hinaus die Funktionen add und remove an. Für das Aktualisieren einer bestehenden Entität muss man keine Funktion aufrufen, es genügt, ihre Werte anzupassen.

Auch in diesem Fall löst Backbone.js verschiedene Ereignisse aus, insbesondere add und remove. Auf sie lässt sich auf dem gleichen Weg zugreifen wie auf die Ereignisse von Entitäten. Die Dokumentation von Backbone.js enthält eine vollständige Liste aller Ereignisse, die Entitäten und Listen auslösen können.

Backbone.js bietet für Listen zudem zahlreiche Funktionen an, um ihren Inhalt zu filtern oder zu sortieren. Dazu zählen insbesondere diverse Funktionen, die aus Underscore.js stammen und von Backbone.js nach außen durchgereicht werden, wie die für zahlreiche Aufgaben ausgesprochen hilfreichen Funktionen map und reduce. Zu guter Letzt verfügen auch Listen über eine toJSON-Funktion, die ihren Inhalt als reines Datenobjekt zurückgibt.

Backbone.js unterstützt es von Haus aus, eigene Logik in einer Entität oder Liste unterzubringen. Hierfür sind keine besonderen Vorkehrungen notwendig. Stattdessen fügt man die gewünschten Eigenschaften und Funktionen der Entität oder der Liste im Aufruf der jeweiligen extend-Funktion hinzu:

var Customer = Backbone.Model.extend({
defaults: {
firstName: '',
lastName:''
},
fullName: function () {
return this.get('firstName') + ' ' + this.get('lastName');
}
});

Besonderes Augenmerk legt Backbone.js darüber hinaus auf das Validieren und Sortieren von Entitäten und Listen. Den wichtigsten Einstiegspunkt für das Validieren von Entitäten stellt die Funktion validate dar, die im Falle einer gültigen Entität undefined, im Fehlerfall jedoch eine entsprechende Fehlermeldung zurückgibt. Auch diese Funktion übergibt man im Rahmen des Aufrufs von extend:

var Customer = Backbone.Model.extend({
defaults: {
firstName: '',
lastName: ''
},
validate: function (attributes, options) {
if (attributes.age < 0) { return 'age must not be negative.'; }
}
});

Die Funktion isValid führt die eigentliche Validierung aus und gibt als logischen Wert zurück, ob die Entität gemäß den in der validate-Funktion hinterlegten Regeln gültig ist oder nicht. Im Fehlerfall kann man dann entsprechend reagieren und den aufgetretenen Fehler mithilfe der Eigenschaft validationError ermitteln:

if (!customer.isValid()) {
console.log(customer.validationError);
}

Zu beachten ist hierbei, dass die set-Funktion keine Validierung auslöst, es sei denn, man übergibt zusätzlich ein passendes Parameterobjekt:

customer.set('age', customer.get('age') + 1, { validate: true });

Für das Sortieren von Entitäten dient in Backbone.js die comparator-Eigenschaft von Listen. Wenn man sie setzt, kümmert sich Backbone.js beim Einfügen von neuen Entitäten in eine Liste automatisch darum, sie sortiert einzufügen, sodass man stets über eine korrekt sortierte Auflistung verfügt.

Um den Wert der comparator-Eigenschaft zu setzen, gibt es drei verschiedene Vorgehensweisen:

  • Im einfachsten Fall weist man ihr den Namen jenes Attributs zu, das man als Sortierkriterium verwenden will. Dieses Vorgehen ist einfach umzusetzen, birgt aber den Nachteil, dass man die Liste ausschließlich nach einem einzigen statischen Kriterium in alphabetischer Reihenfolge sortieren kann.
  • Alternativ kann man der comparator-Eigenschaft eine Funktion zuweisen, die die Entität entgegennimmt und den Namen des Attributs zurückgibt, nach dem man sortieren will. Dabei gelten allerdings die gleichen Einschränkungen wie zuvor. Die einzige Ausnahme stellt die hinzugekommene Möglichkeit dar, das sortierrelevante Attribut dynamisch festzulegen.
  • Als dritte Möglichkeit kann man der comparator-Eigenschaft eine Funktion zuweisen, die zwei Parameter erwartet. Beide Parameter repräsentieren jeweils eine Entität. Auf diesem Weg kann man eine beliebig komplexe Sortierlogik implementieren, wichtig ist einzig der Rückgabewert der Funktion: Der Wert -1 bedeutet, dass die erste Entität vor der zweiten einzusortieren ist; der Wert 1 beschreibt das Gegenteil. Gibt die Funktion hingegen den Wert 0 zurück, bedeutet das, dass beide Entitäten als gleichwertig anzusehen sind.

Nimmt man all dies zusammen, wird klar, dass Backbone.js das Hauptaugenmerk auf die Modellierung der Domäne der Anwendung legt. Bis zu diesem Punkt kann man Backbone.js sogar unabhängig von der Tatsache verwenden, dass es eigentlich zur Implementierung von Single-Page-Anwendungen gedacht ist.

Ein Domänenmodell modellieren zu können, ist hilfreich, genügt für eine vollständige Anwendung jedoch nicht. In der Regel will man es zusätzlich persistieren, meist mit einem REST-Webdienst. Auch hierbei bietet Backbone.js seine Unterstützung an, denn Entitäten verfügen von Haus aus über eine save- und eine destroy-Funktion.

Die save-Funktion serialisiert die zu speichernde Entität und überträgt sie mithilfe einer AJAX-Anfrage im Hintergrund an den Webserver. Abhängig vom Rückgabewert der Funktion isNew der Entität entscheidet Backbone.js, ob es eine POST- oder eine PUT-Anfrage erzeugt – ob es also versucht, die Entität erstmalig zu speichern oder eine bestehende Version zu aktualisieren.

Analog erzeugt die destroy-Funktion eine DELETE-Anfrage, um die Entität vom Webserver zu löschen. Backbone.js benötigt dazu eine URL, an die es die Anfragen schicken kann. Diese URL setzt man mit Hilfe der url-Eigenschaft an der zugehörigen Liste:

var CustomerList = Backbone.Collection.extend({
model: Customer,
url: '/customers'
});

Backbone.js kombiniert die URL und die id-Eigenschaft der betroffenen Entität, um die eigentliche Adresse für eine Anfrage zu ermitteln. Auf dem prinzipiell gleichen Weg befüllt man eine Liste anfänglich mit Daten vom Webserver. Hierzu dient die Funktion fetch, die man allerdings nicht an einer Entität, sondern an der Liste aufruft.

Standardmäßig führt Backbone.js den Inhalt der bestehenden Liste mit den vom Webserver empfangenen Daten zusammen. Falls man die Liste von Grund auf neu aufbauen möchte, ist daher zusätzlich ein entsprechendes Parameterobjekt zu übergeben:

customers.fetch({ reset: true });

Im Hintergrund verwendet Backbone.js für den Datenabgleich mit dem Webserver eine interne Funktion namens Backbone.sync. Sie adressiert standardmäßig einen REST-basierten Webdienst. Bei Bedarf lässt sie sich allerdings durch eine eigene Implementierung ersetzen, um eine beliebige Synchronisationslogik bereitzustellen.

Dazu genügt es, Backbone.sync eine neue Funktion zuzuweisen, die der folgenden Signatur entspricht:

Backbone.sync = function (method, model, options) {
// ...
};

Der Parameter method enthält einen der Werte create, read, update oder delete, der Parameter model hingegen die zu speichernde Entität oder die zu ladende Liste. options schließlich umfasst zusätzliche Optionen, die beim Speichern oder Laden von Daten zu beachten sind.

Alternativ besteht die Möglichkeit, eine derartige Funktion von einem Drittanbieter zu verwenden. So stellt beispielsweise das Projekt Backbone.localstorage eine Funktion zur Verfügung, die auf den lokalen Speicher des Webbrowsers zugreift. Die Integration ist denkbar einfach: Man muss nach dem Laden der Datei backbone.js lediglich die Datei backbone.localstorage.js laden, die die Funktion Backbone.sync wie gewünscht ersetzt:

  [...]
<script type="text/javascript" src="backbone.js"></script>
<script type="text/javascript" src="backbone.localstorage.js"></script>
</body>
[...]

Interessant ist darüberhinaus auch die Möglichkeit, über die Funktion Backbone.ajax eine eigene AJAX-Anbindung anzulegen, beziehungsweise mithilfe der Funktionen Backbone.emulateHTTP und
Backbone.emulateJSON die Kompatibilität zu einigen Webservern verbessern zu können.

Wie bereits erwähnt, kümmert sich Backbone.js an sich nicht um das Aktualisieren der Darstellung im Webbrowser, sondern überlässt diese Aufgabe dem Entwickler und einer von ihm gewählten Bibliothek. Dennoch stellt Backbone.js die Basisklasse Backbone.View zur Verfügung, die zur Definition von Ansichten dient. Eine Ansicht entspricht allerdings eher dem aus MVC bekannten Controller, übernimmt also die Kommunikation zwischen der Webseite und dem Domänenmodell. Zum einen reagiert eine Ansicht auf Eingaben im Webbrowser, indem sie Funktionen am Domänenmodell aufruft. Zum anderen veranlasst sie die Aktualisierung der Darstellung im Webbrowser, wenn Änderungen am Domänenmodell auftreten.

Um eine Ansicht zu definieren, muss man sie analog zu Entitäten und Listen von der Klasse Backbone.View ableiten und einige Eigenschaften definieren: Mindestens tagName ist einzubauen, da die Eigenschaft definiert, als welches HTML-Element die Ansicht gerendert werden soll. Zusätzlich kann man die Eigenschaften className und id angeben, um eine CSS-Klasse und -ID festzulegen:

var CustomerView = Backbone.View.extend({
tagName: 'div',
className: 'customerDetail'
});

Außerdem ist es sinnvoll, die initialize-Funktion zu definieren und in ihr als Reaktion auf eine Änderung des Domänenmodells die render-Funktion aufzurufen. Die Aufgabe dieser Funktion besteht darin, die Darstellung im Webbrowser zu aktualisieren. Wie sie implementiert wird, überlässt Backbone.js jedoch vollständig der Fantasie des Entwicklers:

var CustomerView = Backbone.View.extend({
tagName: 'div',
className: 'customerDetail',
initialize: function () {
this.listenTo(this.model, 'change', this.render);
},
render: function () {
// ...
}
});

In der Regel übernimmt die render-Funktion selbst nicht das eigentliche Aktualisieren, sondern erzeugt lediglich den erforderlichen HTML-Code, um die Ansicht anzuzeigen. Das weist sie der Eigenschaft el beziehungsweise deren jQuery-gekapselter Variante $el zu, die das konkrete HTML-Element repräsentiert, das mit der Ansicht verknüpft wurde. An dieser Stelle bindet man üblicherweise auch die Verwendung von HTML-Vorlagen ein, wobei sich im einfachsten Fall wiederum auf die template-Funktion von Underscore.js zurückgreifen lässt.

Damit man die render-Funktion an übergeordneter Stelle verkettet aufrufen kann, empfiehlt die Dokumentation von Backbone.js, dass die Funktion this an den Aufrufer zurückgibt:

var CustomerView = Backbone.View.extend({
template: _.template(...),
tagName: 'div',
className: 'customerDetail',
initialize: function () {
this.listenTo(this.model, 'change', this.render);
},
render: function () {
this.$el.html(this.template(this.model.attributes));
return this;
}
});

Außerdem lässt sich durch die events-Eigenschaft auf Ereignisse innerhalb des Webbrowsers reagieren, um beispielsweise entsprechende Funktionen an der Ansicht aufzurufen:

var CustomerView = Backbone.View.extend({
template: _.template(...),
tagName: 'div',
className: 'customerDetail',
events: {
'click .save': 'saveCustomer'
},
initialize: function () {
this.listenTo(this.model, 'change', this.render);
},
render: function () {
this.$el.html(this.template(this.model.attributes));
return this;
},
saveCustomer: function () {
// ...
}
});

Beim Erzeugen der konkreten Ansicht kann man dem Konstruktor als Parameter ein bestehendes HTML-Element übergeben, an das sie gebunden wird. Außerdem kann man dem Konstruktor dynamisch die anzuzeigende Entität beziehungsweise die anzuzeigende Liste übergeben, indem man dem Parameterobjekt die Eigenschaft model beziehungsweise collection hinzufügt:

var customerView = new CustomerView({
el: document.getElementById(...),
model: customer
});

Auch für das Verwalten von Routen enthält Backbone.js Unterstützung. Basis hierfür ist die Klasse Backbone.Router, die sich auf dem üblichen Weg ableiten lassen. Als Parameter sind der extend-Funktion ein Objekt mit Routen und die von ihnen aufzurufenden Funktionen zu übergeben:

var AppRouter = Backbone.Router.extend({
routes: {
'customers/:id': 'loadCustomer',
'customers': 'loadCustomers'
},
loadCustomer: function (id) {
// ...
},
loadCustomers: function () {
// ...
}
});

Um einen konkreten Router zu erzeugen, muss man AppRouter als Konstruktor aufrufen:

var appRouter = new AppRouter();

Die Existenz einer Router-Instanz an sich genügt jedoch noch nicht, damit Backbone.js die Navigation des Webbrowsers verwaltet. Darüber hinaus muss man noch die start-Funktion des Objekts Backbone.history aufrufen.

Dieser Funktion kann man dabei als Parameter übergeben, ob man Hashbang nutzende Routen oder die in HTML5 neu eingeführte, ausschließlich mit modernen Webbrowsern kompatible History-API verwenden möchte:

Backbone.history.start({ pushState: true });

Da die Navigation mit Hashbang im Internet Explorer die Verwendung eines iframe-Elements voraussetzt, ist es wichtig, die start-Funktion erst nach dem vollständigen Laden der Webseite aufzurufen. Aus diesem Grund ist man gut beraten, das Instanziieren der Router und den Aufruf der Funktion in einen Aufruf der $-Funktion von jQuery zu kapseln:

$(function () {
var appRouter = new AppRouter();
Backbone.history.start({ pushState: true });
});

Backbone.js hat unbestritten zahlreiche Stärken: Gerade im Vergleich zu Knockout fällt auf, dass Backbone.js nicht nur einen Teilaspekt bei der Entwicklung von Single-Page-Anwendungen adressiert, sondern einen umfassenden Ansatz bietet. Kombiniert mit der klar gegliederten Struktur, die Backbone.js-Anwendungen üblicherweise aufweisen, trägt das viel zur Wartbarkeit von großen Webanwendungen bei.

Der Fokus auf die Modellierung der Domäne und die automatische Synchronisierung mit dem Webserver zwingen den Entwickler, von vornherein eine gewisse Struktur für die Anwendung zu planen und sich Gedanken über den fachlichen Kern zu machen. Dies erklärt, warum mit Backbone.js entwickelte Webanwendungen in der Regel sehr sauber strukturiert sind.

Diese Stärke ist zugleich aber auch eine der relevanten Schwächen von Backbone.js: Da für jede Anwendung zunächst die entsprechende Struktur aufzubauen ist, entsteht insbesondere für kleine Projekte rasch viel Code, der zunächst nichts zu der eigentlichen Anwendung beiträgt, sondern lediglich Infrastrukturcode darstellt. Die Entwicklung ist gerade zu Beginn entsprechend aufwendig und zäh.

Die größte Schwäche von Backbone.js liegt jedoch in dem Verzicht auf ein Konzept zur Aktualisierung der Darstellung. Dadurch entstehen Backbone.js gleich zwei Nachteile. Zum einen ist man wiederum auf die Integration einer Bibliothek eines Drittanbieters angewiesen, was langfristig stets auch zu Kompatibilitäts- und Wartungsproblemen führen kann. Außerdem gibt man die saubere Struktur einer Backbone.js-Anwendung zumindest teilweise zu Gunsten von Code auf, der mit einer anderen Bibliothek entwickelt wurde. Das kann sich insbesondere bei der Verwendung von jQuery schnell rächen.

Zum anderen übernimmt Backbone.js von sich aus keinerlei Verwaltung der dargestellten Ansichten. Das bedeutet, dass man als Entwickler selbst Sorge für deren korrekte Entsorgung tragen muss. All zu rasch entstehen hierbei jedoch nur schwer auffindbare Speicherlecks, die der Stabilität und Leistung der Anwendung langfristig schaden.

Backbone.js begegnet diesen Problemen nicht. Abhilfe schaffen Erweiterungen wie Backbone.Marionette oder Thorax, die allerdings jeweils wiederum alles andere als kompakt sind und die ohnehin schon vorhandene Komplexität der Anwendung weiter erhöhen.

Das Ergebnis sind dann häufig Anwendungen, die zwar sauber strukturiert sind, für wenig Effekt allerdings eine komplexe Infrastruktur und verhältnismäßig viel Code aufweisen.

Alles in allem kann man festhalten, dass Backbone.js ein prinzipiell zwar durchdachtes Framework ist, das allerdings rasch zu unnötig hoher Komplexität führt. Insbesondere der Verzicht auf ein eigenes Konzept zur Verwaltung der Darstellung trägt hierzu bei, unter anderem aufgrund der Speicherverwaltung für einzelne Ansichten, die man von Hand implementieren muss.

Mehr Infos

MVC- und MVVM-Framework für JavaScript im Vergleich

Der vorliegende Artikel ist der zweite Teil einer Serie, in der verschiedene mit JavaScript arbeitende MV*-Frameworks vorgestellt werden. In der nächsten Folge wird das aus dem vergangenen Teil bereits bekannte MVVM-Entwurfsmuster nochmals aufgegriffen, dann allerdings nicht in Verbindung mit Knockout.js, sondern einem zwar noch sehr jungen, aber ausgesprochen vielversprechenden Framework: AngularJS.

Bewertet man Backbone.js im Hinblick auf die Tatsache, dass es bereits im Jahr 2010 die Möglichkeit eröffnet hat, Single-Page-Anwendungen wohlstrukturiert und professionell zu entwickeln, handelt es sich trotz allem um ein durchaus beachtungswürdiges Framework. Betrachtet man es jedoch aus heutiger Sicht, fallen die Komplexität und der erforderliche Aufwand ausgesprochen negativ auf. Vergleicht man Backbone.js zudem mit Knockout.js, so macht sich das fehlende Konzept zur Verwaltung der Darstellung schmerzlich bemerkbar. Backbone.js wirkt dadurch nicht mehr zeitgemäß.

Prinzipiell wäre Knockout daher die perfekte Ergänzung zu Backbone.js, was unter anderem zu dem Projekt Knockback geführt hat. Auch dieser Ansatz birgt allerdings Gefahren, hängt man dann doch gleich von zwei großen Frameworks, deren Kompatibilität zueinander und einer weiteren, dritten Komponente ab, die beide verbindet.

Zweifelsohne eignet sich Backbone.js für komplexe Webanwendungen eher als Knockout, dennoch kann man es auf Grund der genannten Schwächen nur bedingt empfehlen.

Golo Roden
ist Gründer und Geschäftsführer der "the native web UG", eines auf native Webtechniken spezialisierten Unternehmens. Für die Entwicklung moderner Webanwendungen bevorzugt er JavaScript und Node.js und hat mit "Node.js & Co." das erste deutschsprachige Buch zum Thema geschrieben.
(jul)