JavaScript MVC Frameworks - Teil 4

KnockoutJS

KnockoutJS

Im vierten und letzten Teil dieser kleinen Artikelserie betrachten wir KnockoutJS. Ebenfalls wie AngularJS und EmberJS wird es oft als Basis für Single Page Applications empfohlen, beispielsweise im Thoughtworks Technology Radar.

KnockoutJS hat zwar ähnliche Zielplattformen, wenn man es mit AngularJS und EmberJS vergleicht, es geht aber einen etwas anderen Weg: Wie wir später noch genauer sehen werden, ist KnockoutJS viel leichtgewichtiger und bildet eigentlich nur eine dünne Schicht über jQuery oder anderen Selektor-Bibliotheken. Im Gegensatz zu den schwereren Framework-Ansätzen der anderen beiden Kandidaten, ist KnockoutJS eher eine Bibliothek.
KnockoutJS wird von Microsoft entwickelt und hat somit ein sehr solides Umfeld.

Die Rahmenbedingungen klangen für mich auch wieder sehr vielversprechend, also habe ich KnockoutJS ebenfalls ausprobiert

Die Konzepte

Ähnlich wie die anderen beiden Kandidaten unterstützt mich KnockoutJS dabei meine Applikation nach dem Model View ViewModel (MVVM) Pattern zu strukturieren.

Gehen wir doch auch hier anhand des Patterns vor:

Das Model in KnockoutJS ist ein simples Plain-Old-JavaScript-Object. Die Bibliothek macht hier keinerlei Vorgaben.

var myModelComingFromTheServer= {
  artistFromTheServer: ‘Test‘,
  venueFromTheServer: ‘Test2‘
}

Dieses kommt üblicherweise im JSON-Format von einem Server.

Das ViewModel kann in seiner einfachsten Form auch ein Plain-Old-JavaScript-Object sein:

var myViewModel = {
  artist: ‘Test‘,
  venue: ‘Test2‘
}

Durch den Aufruf von:

ko.applyBindings(myViewModel);

wird KnockoutJS dazu angehalten dieses ViewModel mit dem Template abzugleichen (Zum Template komme ich in einer Minute). Dies ist dann nur ein sogenanntes “One-Time-Binding”, Änderungen an den Werten werden nicht nachgehalten.
Spannender wird es, wenn Observables ins Spiel kommen:

var myViewModel = {
  artist: ko.observable(“Test“),
  venue: ko.observable(“Test2“)
}

Mit diesem Konzept haben wir ein Two-Way-Databinding. Auf den Wert des Observables greife ich dann mit myViewMode.artist() zu; geändert wird mit myViewModel.artist(“tes22“).
Etwas komplizierter, aber dennoch ähnlich, funktioniert das Binding für Arrays.

Ähnlich wie bei EmberJS kennt Knockout auch Computed Properties, die – wie der Name schon sagt – aus dem ViewModel berechnete, aber gebundene Werte darstellen:

var myViewModel = {
  dateString: ko.observable(“04.04.2012“),
  formattedDate: ko.computed(function() {...})
}

Jetzt aber endlich zum Templating und dem View Teil des Patterns. Hier bietet KnockoutJS 2 Wege an: Zum einen Knockout-natives Templating (welches ich verwendet habe) oder aber eine Integration einer Third-Party Templating Lösung wie z.B, Underscore (http://knockoutjs.com/documentation/template-binding.html) (welches ich nicht betrachtet habe).

Das native Templating ist etwas ungewöhnlich, dennoch recht pragmatisch – aber für meinen Geschmack zunächst mal mächtig genug.

Markup sieht in KnockoutJS in etwa so aus:

<input type="text" name="venue" data-bind="value: venue">

Das spannende hier ist die Angabe data-bind. Durch den Aufruf von ko.applyBindings(myViewModel) von oben weißt man Knockout an den Wert venue des ViewModels zu tracken.
Man kann auch durch Angabe von jQuery-Selektoren nur bestimmte Bereiche der Seite binden lassen ko.applyBindings(myViewModel,$(‘#meinBereich‘));

Es existieren auch andere Bindings auf z.B. die ‘visible‘-Eigenschaft oder die ‘style‘-Eigenschaft (http://knockoutjs.com/documentation/visible-binding.html):

<div data-bind="visible: shouldBeVisible">... </div>

Falls man den Wunsch hat nicht an ein HTML-Element zu binden (um z.B. nur einen Text anzuzeigen), gibt es die Syntax

<!--ko text: artist--><!--/ko-->

Interessant ist, dass dieses Konzept auch für Schleifen und Kontrollstrukturen durchgezogen wird. Eine Schleife sieht dann beispielsweise wie folgt aus:

<div  data-bind="foreach: loveConcerts">
...
 
</div>

Auch KnockoutJS bietet mithilfe von CustomBindings und Components die Möglichkeit die HTML-Syntax für seine Anwendung aufzubohren und wartbarer zu gestalten. Dies habe ich allerdings im Folgenden nicht weiter verfolgt und verweise hier auf die Dokumentation: http://knockoutjs.com/documentation/component-overview.html und http://knockoutjs.com/documentation/custom-bindings.html

Dies ist eigentlich alles an Konzepten, welche KnockoutJS mitbringt. Es ist viel weniger eigensinnig wie vergleichsweise EmberJS und AngularJS. Und das muss nicht unbedingt schlecht sein.

Für weitere Konzepte wie Routing oder Dependency Verwaltung verweist KnockoutJS auf andere kleine Bibliotheken wie Pager.js für das Routing oder Require.js für die Dependency-Verwaltung.

Das Ergebnis

Aber nun mal direkt zur Umsetzung der Konzertapplikation mittels KnockoutJS. Ich habe für die Umsetzung die Version 2.3.0 verwendet.
Die Sourcen liegen unter https://github.com/holgergp/concertKnockout und die Anwendung ist zu sehen unter http://concertknockout.herokuapp.com/

Ähnlich wie bei den anderen Ansätzen, wollte ich mir auch hier die Struktur der Anwendung mithilfe von yeoman generieren lassen. Hierfür habe ich den inoffiziellen Generator unter https://github.com/bmac/generator-knockout verwendet.
Dieser lief vergleichsweise prima und ohne nennenswerte Zwischenfälle. Erwähnenswert ist, dass dieser Generator das Projekt direkt mit der oben erwähnten Dependency-Management-Bibliothek require.js anlegt. Im Vergleich zum EmberJS-Generator verzichtet dieser allerdings darauf die Sourcen zu compilieren/minfizieren. Für den Einstieg und für die Übersicht sehr schön.

Auf ein “richtiges” Modell habe ich hier verzichtet und habe mich auf ein ViewModel beschränkt.
Hierfür habe ich pro Konzertart eine Funktion vorgesehen. Die jeweiligen Konzerte werden als “obervableArray” hinterlegt, wobei ich Testdaten jeweils hart vorgefüllt habe.
Die “ViewLogik”-Formatierung des Datums und Kennzeichnen der Fälligkeit eines Konzerts habe ich als “computedProperty“ umgesetzt. Das klappte hier problemlos.
Für den jeweiligen Konzertbereich habe ich Knockout, wie in der Einleitung beschrieben über ko.applyBindings(loveConcertsViewModel, $('#loveConcertSection')[0]); aktiviert (https://github.com/holgergp/concertKnockout/blob/master/app/main.js#L138)

Interessant ist noch der vierte Bereich, das Formular für die “NewConcerts”. Hier definiere ich ein neues Konzertelement, welches in die oben definierten Arrays verschoben wird. (https://github.com/holgergp/concertKnockout/blob/master/app/main.js#L152-L169)
Das Binding der einzelnen Properties habe ich über ko.observable(property); angewiesen.

Drag-and-drop war etwas tricky umzusetzen. Ich habe hierfür die nur knapp dokumentierte Bibliothek von one.com verwendet. Richtige Überraschungen sind aber auch hier nicht aufgetreten.
Das Template habe ich strukturell der Einfachheit halber von meiner AngularJS-Lösung übernommen, und das klappte auch recht gut. Es gilt hierfür Ähnliches wie für die AngularJS-Umsetzung: Wir haben, wie oben schon angedeutet, drei Bereiche für die Konzerte und einen für die neuen Konzerte. Die Drag-and-drop-Lösung ist ebenso wie im “Original” nicht selbstverständlich.
Etwas merkwürdig finde ich die kommentarartige Syntax für das Textbinding, das ist aber Geschmackssache (https://github.com/holgergp/concertKnockout/blob/master/index.html#L119-L122).

Auch der Service und Provider sind von meiner AngularJS Lösung “übriggeblieben”, diese sind kein Mittel von KnockoutJS. Ich fand diese nur ein ordentliches Mittel zur Strukturierung (https://github.com/holgergp/concertKnockout/blob/master/app/main.js#L186-L220).

Erwähnenswert fand ich noch die Dependency-Verwaltung per Require.js. Dies ist nicht ganz selbsterklärend, aber durchaus schön: Injektion hier: https://github.com/holgergp/concertKnockout/blob/master/app/main.js#L4 und die Konfiguration hier: https://github.com/holgergp/concertKnockout/blob/master/app/config.js

Für die Unit Tests wird die Verwendung von QUnit empfohlen. Die Kombination aus QUnit und require.js lief nicht Out-of-the-box, da das Laden der Abhängigkeiten ja komplett in requiere.js Hand ist. Dies war aber schlussendlich recht einfach ans Rennen zu bringen.

Insgesamt gefällt mir die Lösung mit KnockoutJS sehr gut, die Entwicklung fühlt sich recht zügig an. Allerdings muss man wissen, wo die Grenzen von KnockoutJS sind, beispielsweise greift man bereits für FormValidation auf Plugins oder jQuery Mittel zurück. Das Abstraktionsniveau ist niedriger, dafür erscheint mir die Lösung recht klar. Man darf nicht außer Acht lassen, dass KnockoutJS “nur” eine Bibliothek ist.
Wenn Sie sich für ein Framework mit den Mitteln von KnockoutJS als Unterbau interessieren, könnte für Sie das Framework Durandal oder dessen Nachfolger Aurelia interessant sein.
KnockoutJS ist für mich ein guter Kandidat, wenn man wirklich nur diese kleine Schicht über jQuery in seinem Projekt benötigt oder vielleicht bestehenden jQuery Code strukturieren möchte und dafür kein schwergewichtiges Framework einsetzen möchte.

Fazit

Mein Fazit für KnockoutJS und die Konzertanwendung. Ich verwende hier wie gehabt die Skala + (gut), 0 (ok), – (nicht so gut):

Einarbeitung: +

  • KnockoutJS ist recht übersichtlich.
  • Einige Aspekte wie die ViewModels versteht man vielleicht nicht sofort.
  • Das Tutorial auf http://learn.knockoutjs.com/ gefällt mir sehr gut.

Wartbarkeit: 0

  • Da KnockoutJS wenig Vorgaben macht, hängt dies stark von Ihrem Projekt ab. Ich schätze aber, dass ein KnockoutJS-Projekt wartbarer sein kann, als ein reines jQuery-Projekt.

Lassen sich auch anspruchsvolle Anforderungen umsetzen: 0

  • Ja, allerdings gibt es weniger Features out-of-the-box (z.B.: FormValidation).
  • Drag-and-drop war etwas tricky, aber nicht viel schwieriger als bei den anderen Kandidaten.

Wie gut lässt sich die Anwendung testen? 0

  • Hier gilt gleiches wie für EmberJS:
    • Ja, eine KnockoutJS-Anwendung lässt sich gut testen, allerdings steht dieses nicht so im Vordergrund wie bei AngularJS.

Wie groß ist die Community? 0

  • Auch hier gilt Ähnliches wie für EmberJS:
    • Kleinere Community als bei AngularJS, aber ich habe auf alle Fragen gute Antworten gefunden
    • Auf Stackoverflow gibt es zu Knockout.js ca. 14.000 Fragen (Stand 22.06.2015).

Wie gut ist die Dokumentation? +

  • Die Dokumentation gefällt mir gut, muss allerdings auch weniger leisten, als bei den beiden anderen Kandidaten.

Am Ende

Vielen Dank, dass Sie es bis hierhin durchgehalten haben! Ich hoffe, Sie konnten ein wenig mitnehmen und hatten – wie auch ich – ein wenig Spaß dabei. Ich hatte mit allen drei Ansätzen Freude. Ich bin nach wie vor überzeugt, dass AngularJS eine gute Wahl ist, obwohl die Versionspolitik diese Freude leicht getrübt hat.

EmberJS konnte mich persönlich im Vergleich noch nicht so überzeugen, es ist aber nichtdestotrotz ähnlich mächtig wie AngularJS. Insbesondere ist es, falls Sie einen Background in der Rails Entwicklung haben und mit der dort vorherrschenden Convention over Configuration Philosophie vertraut sind, eine ausgezeichnete Wahl.

KnockoutJS ist dann eine ganze Ecke kleiner als die beiden Kontrahenten, macht aber genau deswegen Spaß! Nicht nur für kleinere Projekte und für die Strukturierung von auf Legacy jQuery Code basierende Projekte eine exzellente Wahl.

Als weiterer interessanter Kandidat hat sich in den letzten Monaten mit Facebooks react.js hervorgetan, den man sicherlich im Auge behalten muss.


Jetzt teilen: