Yanick Dickbauer
Software Developer bei Carrot & Company. Schreibt über tech stuff.
12 Minuten

Ein Angular 9 Server Side Rendering Projekt

12 Minuten
Artikel am 11. November 2019 gepostet
Bei einer langsamen Verbindung lädt die erste Seite oft ewig. Server-Side-Rendering kann helfen. Wie man es einrichtet, wann man es nicht tun sollte und wie man es mit den Vorteilen von Single Page Applications kombiniert.

UPDATE: 10.4.2020, Angular 9

Dieses Tutorial folgt den offiziellen Anweisungen im Universal Guide von Angular und liefert gerenderten Code über das Node.js Express Framework . Wer Nrwl Nx vewendet, sollte diesen Abschnitt überspringen und den Anweisungen unter Nx workspace schematics: server ide rendering (SSR) with Angular Universal folgen.

Erstellen eines Angular-Projekts mit eingebauter SSR Funktionalität

Als Erstes erstellen wir ein neues Angular-Projekt mittels der Angular CLI und fügen ihm einfach Universal Support hinzu.

ng new frontend
cd frontend
ng add @nguniversal/express-engine --clientProject frontend

Der obige Befehl hat unter anderem das Sourcefile server.ts auf der obersten Ebene unseres Angular-Projekts hinzugefügt. Dieses File wird der Einstiegspunkt des Node Servers sein, der unsere gerenderten Seiten liefert.

Wir können nun npm run build:ssr && npm run serve:ssr ausführen, um das Projekt zu erstellen und unser Angular-Projekt lokal zu testen. Wenn wir nun auf http://localhost:4200 browsen und den Netzwerkverkehr analysieren, können wir das Resultat bereits sehen:

First page

Die erste Antwort des Servers (nach 20ms) beinhaltet serverseitig gerendertes Html, und das ohne Änderung des Source Codes! Sobald die gebündelten Javascript-Dateien geladen und interpretiert werden, wird das serverseitig gerenderte AppComponent automatisch mit dem, nun im Browser gerenderten Componentcode ersetzt. Dieses Verhalten lässt sich testen, indem ihr in eurem Browser das Javascript deaktiviert .

Ein neues Component hinzufügen

Wir erstellen nun eine, unter /public/ erreichbar Angular-Route, welche ein Component namens PublicPageComponent anzeigt. Dieses Component wird das Einzige sein, welche serverseitig gerendert wird. PublicPageComponent gibt den Namen des aktuellen Page-Renderers aus (Browser/Server). Surft man also auf /public , wird zunächst Rendered by Server und im nächsten Moment Rendered by Browser angezeigt:

Wir erzeugen das Component

cd src
ng g c PublicPage --module app

und fügen zu dieses in app-routing.module.ts ein:

const routes: Routes = [{ path: "public", component: PublicPageComponent }];

Schließlich ändern wir den Code der Component, um den aktuellen Renderer anzuzeigen:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
import { Component, Inject, OnInit, PLATFORM_ID } from "@angular/core";
import { isPlatformBrowser } from "@angular/common";

@Component({
  selector: "app-public-page",
  template: "<p>Rendered by {{ renderer }}</p>",

  styleUrls: ["./public-page.component.sass"]
})
export class PublicPageComponent {
  renderer: string;

  constructor(@Inject(PLATFORM_ID) platformId: any) {
    this.renderer = isPlatformBrowser(platformId) ? "Browser" : "Server";
  }
}

Einrichten eines Nginx-Services, der Requests an den node-js-Server weiterleitet

Um nur einige Teile unserer Website auf der Server-Seite zu rendern, müssen wir mindestens zwei Services laufen haben: Einen Webserver, der sich wie ein Proxy verhält und auf Anfragen des Client-Browsers antwortet und einen anderen, der den Rendering-Prozess bei Bedarf ausführt. Der Einfachheit halber wird der Proxy den gesamten Content bis auf /public ausliefern.

Die folgende Grafik bietet einen Überblick der zwei Services:

Setting of our services

Deshalb erstellen wir einen Nginx -Service, der die kompilierten Output-Files unserer Angular Website ausliefert.

Wenn wir die folgende Command ausführen

npm run build:ssr

bekommen wir die Output-Files unter:

frontend
+-- dist
    +-- browser
        ... (static files, Angular`s compile output)
    +-- server
        main.js  (Angular SSR bundle)
    server.js    (-> node.js express engine)

Um den Proxy-Dienst einzurichten, erstellen wir eine Standard-Nginx-Instanz und kopieren Dateien von frontend/dist/browser in den HTML-Root Ordner (man kann dieses Dockerfile verwenden, um einen Container einzurichten).

In unserem Fall ist der Root Ordner /usr/share/nginx/html . Daher müssen wir Nginx so konfigurieren, dass es alles aus diesem Ordner heraus bedient, aber /public HTTP-Requests an den Angular SSR Node Service weiterleitet.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
server {
  listen 80;

  location / {
    root /usr/share/nginx/html;
    index index.html index.htm;
    try_files $uri $uri/ /index.html =404;
  }


  # Forward requests to the node service which
  #     renders on the server side:
  location ~ ^/(public)$ {
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header Host $http_host;
    proxy_redirect off;
    proxy_pass http://internal-hostname-of-ssr-server:4000;
  }

  include /etc/nginx/extra-conf.d/*.conf;
}

Hinweis: Sollen mehrere Seiten serverseitig gerendert werden, so muss Zeile 13 dementsprechend angepasst werden.

Einen NodeJS-Service für SSR einrichten

Schließlich müssen wir noch den Dienst einrichten, der /public auf der Serverseite rendert. Nichts könnte einfacher sein - wir haben es schon einmal gemacht:

Durch das Ausführen von

npm run serve:ssr

startet das Service. Aufpassen bei aktivem Output-Filename-Hashing: Node benötigt die gleichen Output-Dateien wie das Nginx-Service. Folgendes sollte man daher nicht noch einmal ausführen:

npm run build:ssr

Vollständiges Projektbeispiel mit Docker Containern:

Der obige Code mit allen notwendigen Einstellungen findet sich in folgendem Beispiel-Repository: https://github.com/carrotandcompany/angular-partial-ssr-example .

Glaubt ihr, Server Side Rendering könnte euch bei euren Projekten helfen, und braucht ihr Hilfe beim Aufsetzen?

Wir verwenden Cookies 🍪