Verbesserte Performance, Reichweite und Suchmaschinen-Unterstützung

AngularServices: Serverseitiger Betrieb von Angular 2
Kommentare

AngularServices erlauben den serverseitigen Betrieb von Angular 2 innerhalb einer ASP.NET-Anwendung. Durch das serverseitige Vorrendern von Views verbessern sich die Performance, die Reichweite und die Unterstützung für Suchmaschinen.

Gerade wenn es um Web-basierte und plattformübergreifende Anwendungen geht, sind Single-Page-Applications (SPA) derzeit ein äußerst beliebter Ansatz. Sie funktionieren ähnlich wie klassische verteilte Anwendungen und senken somit die Einstiegshürde für Desktop-Entwickler. Daneben erlauben Sie durch den exzessiven Einsatz von JavaScript eine hohe Benutzerfreundlichkeit und bieten rasche Reaktionszeiten.

Wo Licht ist, findet sich jedoch auch Schatten: Benutzer, die JavaScript deaktiviert haben, können eine SPA in der Regel gar nicht nutzen. Während Benutzer Webanwendungen in der Regel die Ausführung von JavaScript zugestehen, ist das bei offenen Webangeboten nicht immer der Fall. Dies ist auch der Grund, warum solche Seiten nicht oder nur teilweise auf den SPA-Ansatz setzen.

Ein weiterer Nachteil von SPAs ist die (gefühlte) Startzeit, die beim Einsatz clientseitigen Renderings länger ausfällt als bei klassischen Webanwendungen. Auch Suchmaschinen können mit Web-1.0-Seiten noch immer besser umgehen, und Linkvoransichten lassen sich damit einfacher realisieren.

Isomorphic und Universal JavaScript

Die eingangs genannten Schattenseiten lassen sich durch Einführung eines zusätzlichen serverseitigen Rendering Layers kompensieren. Das bedeutet, dass die Serverseite in der Lage sein muss, den JavaScript-Code der SPA auszuführen. Hierbei ist von Isomorphic oder Universal JavaScript die Rede.

Schnell und überall: Datenzugriff mit Entity Framework Core 2.0

Dr. Holger Schwichtenberg (www.IT-Visions.de/5Minds IT-Solutions)

C# 7.0 – Neues im Detail

Christian Nagel (CN innovation)

Bekannte Frameworks wie React, Meteor oder Ember machen es bereits vor: Beim Abrufen der SPA rendert der Server zumindest die erste Ansicht vor. Somit muss dafür clientseitig kein Rendering oder Nachladen weiterer Daten erfolgen, und die Startzeit erhöht sich. Weitere Aktionen führt die Anwendung dann clientseitig aus, wobei auch die Möglichkeit besteht, weitere Ansichten vom Server rendern zu lassen. Das ergibt zum Beispiel bei äußerst komplexen Ansichten Sinn. Darüber hinaus können somit auch Benutzer, die entweder JavaScript deaktiviert haben oder Legacy-Browser einsetzen, in den Genuss der Seite kommen. Es liegt auf der Hand, dass bei einem kompletten serverseitigen Rendering nicht sämtliche Möglichkeiten zur Verfügung stehen. Aber immerhin ist die Seite zugreifbar, sofern sie dahingehend entworfen wurde. Dieses Vorgehen ist auch von klassischen Seiten bekannt, die AJAX-Techniken zur Steigerung der Benutzerfreundlichkeit nutzen.

Angular 2 Universal

Mit dem Projekt Angular 2 Universal zieht die Idee von Isomorphic bzw. Universal JavaScript auch in die Welt von Angular 2 ein. Es handelt sich dabei um ein quelloffenes Projekt von Entwicklern aus der Angular-2-Community. Da das Angular-2-Team die Entwicklung dieses auf Node.js basierenden Projekts begleitet und sich auch am Entwurf beteiligt hat, kommt ihm der Stellenwert eines De-facto-Standards zu. Möglich sind solche Vorhaben durch die Architekturentscheidung, das Rendering in Angular 2 austauschbar zu gestalten (Abb. 1). Somit können neben dem DomRenderer, der im Browser zum Einsatz kommt, weitere Renderer genutzt werden. Angular 2 Universal stellt beispielsweise einen ServerDomRenderer zur Verfügung, der vorgerendertes Mark-up retourniert.

steyer_dotnet_1

Abb. 1: Austauschbare Renderer in Angular 2

Weitere Renderer

Neben den hier besprochenen Renderern wird es noch weitere Implementierungen für Angular 2 geben. Beispielsweise findet sich in den Sourcen des SPA-Frameworks ein Renderer, der zur Steigerung der Performance in einem eigenen Webworker läuft. Darüber hinaus entsteht gerade ein Renderer für NativeScript zum Realisieren nativer mobiler Anwendungen.

Beim Einsatz des ServerDomRenderers darf die Angular-2-Anwendung nicht direkt auf das Browser-DOM zugreifen. Der Grund dafür ist einfach: Das Browser-DOM steht am Server nicht zur Verfügung. Aus diesem Grund sieht die Architektur von Angular 2 hierzu eine Indirektion vor. Die folgenden Codezeilen, die aus dieser Quelle übernommen wurden, veranschaulichen den Umgang damit:

constructor(element: ElementRef, renderer: Renderer) {
  renderer.setElementStyle(element, 'fontSize', 'x-large');
}

Der gezeigte Konstruktor lässt sich ein ElementRef-Objekt injizieren, das das DOM-Element der aktuellen Komponente referenziert. Anstatt dieses direkt zu verändern, gibt es dem ebenfalls injizierten Renderer die gewünschte Modifikation bekannt.

Beim Einsatz des von Angular 2 Universal verfolgten Ansatzes ergibt sich die Problemstellung, dass ein wenig Zeit zwischen dem Anzeigen der ersten empfangenen Teile und dem Abschluss des Ladevorgangs besteht. Erst wenn der Ladevorgang abgeschlossen ist, kann die clientseitige Anwendung starten und die Dialogsteuerung übernehmen. Benutzerinteraktionen, die in dieser Zeitspanne stattfinden, laufen somit ins Leere.

Um das zu verhindern, beherbergt Angular 2 Universal ein Unterprojekt namens preboot.js. Es zeichnet Benutzeraktionen auf und spielt diese dann nach dem Start der clientseitigen Anwendung erneut ab.

AngularServices

Da Angular-Connect auf Node.js basiert, lässt es sich nicht per se gemeinsam mit ASP.NET-Projekten betreiben. Die AngularServices, die gerade bei Microsoft entstehen, versuchen diese Lücke zu schließen. Vorgestellt wurden sie zum MVP Summit im Herbst 2015 von Steve Sanderson, der sich bei Microsoft seit Jahren mit Single Page Applications beschäftigt und bei der Entwicklung des Azure-Portals beteiligt war. Die Aufzeichnung dieser Präsentation findet sich hier. In dieser betont Sanderson, dass es sich derzeit um eine sehr frühe Version handelt, die in erster Linie dem Sammeln von Erfahrungen und Feedback dient. Dazu kommt, dass es auf Angular 2 Universal basiert, das genau wie Angular 2 noch nicht in einer finalen Version vorliegt.

Als Grundlage nutzen AngularServices die so genannten NodeServices, die die Ausführung von Node.js innerhalb von ASP.NET erlauben. Neben den NodeServices und den AngularServices befindet sich im GitHub Repository des Vorhabens [5] auch ein Projekt namens ReactServices, das die serverseitige Nutzung von React innerhalb einer ASP.NET-Anwendung erlaubt.

Zusätzlich beinhaltet das Repository auch Beispielprojekte. Eines dieser Beispielprojekte ist eine Version des Music-Store-Samples, mit dem das Produktteam für gewöhnlich die Möglichkeiten von ASP.NET präsentiert. Diese Version basiert auf Vorabversionen von Angular 2 sowie ASP.NET MVC 6 und veranschaulicht die Funktionsweise von AngularServices.

Nutzung von AngularServices

Da es derzeit noch keine Projektvorlage für AngularServices gibt, bietet sich das vorhin erwähnte Music Store Sample aus dem Repository für erste Schritte an. Die nötigen Schritte zur Inbetriebnahme des Projekts sind im „Readme“ des Repositories beschrieben. Es empfiehlt sich, den darin zu findenden Anweisungen penibel zu folgen. Dabei ist der in Klammern gehaltene Hinweis, Abhängigkeiten nicht via Visual Studio, sondern durch einen Aufruf von npm install auf der Kommandozeile manuell zu laden, besonders ernst zu nehmen.

Das Beispiel referenziert die AngularServices, die sich in einem anderen Projekt derselben Solution befinden, und bietet über die View des HomeControllers eine auf Angular 2 basierende SPA an. Damit AngularServices eine beliebige Seite der Single-Page-Anwendung serverseitig rendern können, leitet eine mit der Hilfsmethode MapSpaFallbackRoute definierte Route sämtliche Anfragen an diese Action-Methode weiter:

routes.MapRoute("default", "{controller}/{action}/{id:int?}");
routes.MapSpaFallbackRoute("spa-fallback",
  new { controller = "Home", action = "Index" });

Damit das funktioniert, ist für das Routing innerhalb der SPA die PathLocationStrategy zu nutzen. Diese platziert den URL der aktuellen Route nicht im Hash-Fragment, sondern unter Einsatz des HTML5-History-API direkt innerhalb des URLs. Somit landet beim initialen Abrufen der SPA der URL der gewünschten Route am Server. Dasselbe passiert auch bei weiteren Anfragen, wenn JavaScript deaktiviert ist.

Einen Auszug der View findet sich in Listing 1. Das Element app repräsentiert die Root-Komponente der Angular-2-Anwendung. Der Tag-Helper asp-ng2-prerender-module gibt an, dass diese Komponente serverseitig vorgerendert werden soll. Dazu verweist sie auf die Datei, die die Root-Komponente beherbergt. Dabei ist zu beachten, dass diese Datei nicht das serverseitige Bootstrapping der Anwendung durchführt. Dafür ist eine JavaScript-Datei innerhalb der AngularServices verantwortlich. Somit kann der Entwickler auf den serverseitigen Bootprozess keinen Einfluss nehmen, ohne AngularServices zu modifizieren. Dass das nicht wünschenswert ist und sich im Laufe der Weiterentwicklung noch ändern muss, liegt auf der Hand. Das clientseitige Bootstrapping erfolgt hingegen durch Einbinden der Datei bootstrap am Ende.

Zusätzlich kommt im betrachteten Beispiel der Tag-Helper cache zum Einsatz. Dieser kümmert sich um das Caching der vorgerenderten SPA und soll somit die Performance zusätzlich verbessern. Der Parameter vary-by gibt an, dass pro URL und somit pro Route ein eigener Cacheeintrag einzurichten ist.

Damit die vorgerenderte Seite nach dem Eintreffen im Browser keine weiteren HTTP-Zugriffe zum Abrufen von benötigten Daten durchführen muss, führt das Beispiel mit der Methode PrimeCache ein so genanntes Cache Priming durch. Das bedeutet, dass sie die später benötigten Daten in einen Cache lädt. Dieser besteht aus einem Skriptbereich, der diese Daten in einer globalen Variable verstaut. Zur Nutzung dieses Caches erhält die Anwendung beim Bootstrapping eine von den AngularServices bereitgestellte Variante des HTTP-Service.

<cache vary-by="@Context.Request.Path">
  <app asp-ng2-prerender-module="wwwroot/ng-app/components/app/app">
    Loading...
  </app>
</cache>
@section scripts {
  @await Html.PrimeCache(Url.Action("GenreMenuList", "GenresApi"))
  @await Html.PrimeCache(Url.Action("MostPopular", "AlbumsApi"))
  […]
  <script src="~/lib/angular2/bundles/angular2.dev.js"></script>
  […]
  <script>System.import('./ng-app/components/app/bootstrap');</script>
}

Damit die Tag-Helper von AngularServices zur Verfügung steht, sind diese in der Datei Views\_ViewImports.cshtml zu referenzieren. Die nachfolgenden Codezeilen veranschaulichen das:

@using MusicStore
@using Microsoft.AspNet.AngularServices
@addTagHelper "*, Microsoft.AspNet.Mvc.TagHelpers"
@addTagHelper "*, Microsoft.AspNet.AngularServices"

Ein weiteres Merkmal des besprochenen Beispiels ist, dass die via npm bezogenen Bibliotheken, wie Angular 2 oder Angular 2 Universal, per GULP ins Verzeichnis wwwroot\lib kopiert werden. Von dort aus werden die Bibliotheken, wie am Ende von Listing 1 gezeigt, in den Browser geladen. Die für diesen Kopiervorgang notwendigen Anweisungen finden sich, wie bei Nutzung von GULP üblich, in der Datei gulpfile.js.

Resultat

Das Resultat des hier beschriebenen Unterfangens ist eine SPA-Version der Music-Store-App, die die erste Ansicht zur Steigerung der Performance vorrendert und teilweise ohne JavaScript funktioniert. Beispielsweise erlaubt die hier betrachtete Anwendung in diesem Fall zumindest ein Navigieren zwischen einzelnen Seiten und somit Routen über Links.

Das Verhalten dieser Anwendung lässt sich einfach durch Deaktivieren von JavaScript im Browser aber auch durch Protokollierung der übertragenen Nachrichten nachvollziehen.

Zusammenfassung und Fazit

Die AngularServices, die gerade bei Microsoft entstehen und in einer frühen Version auf GitHub zu finden sind, ermöglichen den serverseitigen Betrieb von Angular 2. Somit können SPAs die erste Ansicht komplett serverseitig rendern und anschließend auf einen clientseitigen Betrieb wechseln. Das verbessert die (wahrgenommene) Ladezeit sowie die Unterstützung für Suchmaschinen und macht eine Linkvorschau einfacher. Daneben funktionieren solche Anwendungen auch teilweise ohne JavaScript. Inwieweit die Handhabung von Formularen ohne clientseitiges JavaScript unterstützt wird, bleibt abzuwarten. Genauso bleibt abzuwarten, wie sich AngularServices weiterentwickeln und in welcher Form sie schlussendlich zur Verfügung stehen.

Aufmacherbild: An Network servers in data room via Shutterstock / Urheberrecht: panumas nikhomkhai

Unsere Redaktion empfiehlt:

Relevante Beiträge

Meinungen zu diesem Beitrag

X
- Gib Deinen Standort ein -
- or -