Dynamisches Rendering mit Rendertron

Donnerstag, 31. Januar 2019

Viele Front-End-Frameworks setzen zum Anzeigen von Inhalten JavaScript voraus. Das bedeutet, dass die Indexierung eurer Inhalte oder die Aktualisierung der indexierten Inhalte durch Google möglicherweise länger dauert.

Eine Möglichkeit, dies zu umgehen, haben wir dieses Jahr bei der Google I/O diskutiert: dynamisches Rendering. Die Implementierung ist auf vielerlei Arten möglich. In diesem Blogpost implementiere ich dynamisches Rendering beispielhaft anhand von Rendertron, einer Open-Source-Lösung auf Basis der Headless-Variante von Chromium.

Für welche Websites kommt dynamisches Rendering infrage?

Nicht alle Suchmaschinen oder Bots von sozialen Netzwerken, die eure Website besuchen, können JavaScript ausführen. So benötigt etwa der Googlebot unter Umständen etwas Zeit, um euer JavaScript auszuführen. Außerdem gelten einige Einschränkungen.

Dynamisches Rendering empfiehlt sich für häufig wechselnde Inhalte, die sich nur mit JavaScript anzeigen lassen. Möglicherweise käme für eure Website auch eine Hybridlösung wie Angular Universal infrage, um etwa die Zeit bis zum First Meaningful Paint zu verkürzen.

Wie funktioniert dynamisches Rendering?

Funktionsweise des dynamischen Renderings

Beim dynamischen Rendering wird zwischen clientseitig gerenderten und vorgerenderten Inhalten für bestimmte User-Agents gewechselt.

Ihr benötigt einen Renderer, um JavaScript auszuführen und statische HTML-Inhalte zu erzeugen. Rendertron ist ein Open-Source-Projekt, das zum Rendern die Headless-Variante von Chromium verwendet. Apps mit nur einer Seite laden Daten oft im Hintergrund oder stellen Aufgaben zurück, um ihre Inhalte zu rendern. Die Mechanismen von Rendertron können feststellen, wann das Rendering einer Website abgeschlossen ist. Es wird gewartet, bis alle Netzwerkanfragen beendet und alle Aufgaben erledigt sind.

Themen dieses Blogposts:

  1. Blick auf eine Beispiel-Web-App
  2. Einrichten eines kleinen express.js-Servers zur Bereitstellung der Web-App
  3. Installieren und Konfigurieren von Rendertron als Middleware für dynamisches Rendering

Beispiel-Web-App

Die Web-App "Kitten Corner" nutzt JavaScript, um eine Auswahl von Katzenbildern aus einer API zu laden. Diese werden dann in einem Raster angezeigt.

Süße Katzenbilder in einem Raster und eine Schaltfläche zum Anzeigen weiterer Bilder – diese Web-App lässt keine Wünsche offen.

So sieht der JavaScript-Code aus:

const apiUrl = 'https://api.thecatapi.com/v1/images/search?limit=50';
   const tpl = document.querySelector('template').content;
   const container = document.querySelector('ul');
   function init () {
     fetch(apiUrl)
     .then(response => response.json())
     .then(cats => {
       container.innerHTML = '';
       cats
         .map(cat => { const li = document.importNode(tpl, true); li.querySelector('img').src = cat.url; return li;
         }).forEach(li => container.appendChild(li));
     })
   }
   init();
   document.querySelector('button').addEventListener('click', init);

Die Web-App verwendet modernes JavaScript (ES6). Dieses wird vom Googlebot noch nicht unterstützt. Anhand des Tests auf Optimierung für Mobilgeräte lässt sich herausfinden, ob der Inhalt für den Googlebot sichtbar ist:

Der Test ergibt, dass die Seite für Mobilgeräte optimiert ist, aber auf dem Screenshot ist nicht eine einzige Katze zu sehen. Überschrift und Schaltfläche sind vorhanden, aber keine Katzenbilder.

Zwar lässt sich dieses Problem ganz einfach beheben, dennoch ist es eine gute Übung, was die Einrichtung von dynamischem Rendering betrifft. Dank dynamischem Rendering werden die Katzenbilder für den Googlebot sichtbar, ohne Änderungen am Code der Web-App vornehmen zu müssen.

Server einrichten

Zum Bereitstellen der Web-App verwenden wir express, eine node.js-Bibliothek zum Erstellen von Webservern.

Der Servercode sieht so aus (den gesamten Projektquellcode findet ihr hier):

const express = require('express');
const app = express();
const DIST_FOLDER = process.cwd() + '/docs';
const PORT = process.env.PORT || 8080;
// Serve static assets (images, css, etc.)
app.get('*.*', express.static(DIST_FOLDER));
// Point all other URLs to index.html for our single page app
app.get('*', (req, res) => {
  res.sendFile(DIST_FOLDER + '/index.html');
});
// Start Express Server
app.listen(PORT, () => {
  console.log(`Node Express server listening on https://localhost:${PORT} from ${DIST_FOLDER}`);
});

Ihr könnt das Livebeispiel hier testen. Es sollten eine Reihe von Katzenbildern zu sehen sein, wenn ihr einen modernen Browser verwendet. Damit ihr das Projekt über euren Computer ausführen könnt, benötigt ihr node.js zum Ausführen der folgenden Befehle:

npm install --save express rendertron-middleware
node server.js

Anschließend verweist ihr euren Browser auf https://localhost:8080. Jetzt ist es an der Zeit, das dynamische Rendering einzurichten.

Rendertron-Instanz erstellen

Der von Rendertron ausgeführte Server gibt für eine URL statische HTML-Inhalte zurück. Dazu wird die Headless-Variante von Chromium verwendet. Wir folgen der Empfehlung aus dem Rendertron-Projekt und verwenden die Google Cloud Platform.

Zum Erstellen eines neuen Google Cloud Platform-Projekts gibt es ein Formular.

Für den Einstieg steht euch die kostenlose GCP-Stufe zur Verfügung, bei Verwendung dieses Set-ups in einer Produktionsumgebung können Kosten gemäß Google Cloud Platform-Preisliste anfallen.

  1. Erstellen Sie ein neues Projekt in der Google Cloud Console. Schreibt euch die Projekt-ID auf, die ihr unterhalb des Eingabefelds findet.
  2. Installiert das Google Cloud SDK wie in der Dokumentation beschrieben und meldet euch an.
  3. Klont das Rendertron-Repository aus GitHub anhand des folgenden Befehls:
    git clone https://github.com/GoogleChrome/rendertron.git
    cd rendertron
  4. Führt die folgenden Befehle aus, um Abhängigkeiten zu installieren und Rendertron auf eurem Computer zu erstellen:
    npm install && npm run build
  5. Aktiviert den Cache von Rendertron. Dazu erstellt ihr eine neue Datei namens config.json mit folgendem Inhalt im Rendertron-Verzeichnis:
    { "datastoreCache": true }
  6. Führt den folgenden Befehl im Rendertron-Verzeichnis aus. Ersetzt YOUR_PROJECT_ID durch die Projekt-ID aus Schritt 1.
    gcloud app deploy app.yaml --project YOUR_PROJECT_ID
  7. Wählt eine Region aus und bestätigt die Bereitstellung. Wartet, bis der Vorgang abgeschlossen ist.
  8. Gebt die URL YOUR_PROJECT_ID.appspot.com ein. Ihr solltet die Benutzeroberfläche von Rendertron mit einem Eingabefeld und ein paar Schaltflächen sehen.
Benutzeroberfläche von Rendertron nach der Bereitstellung auf der Google Cloud Platform

Wenn ihr die Rendertron-Weboberfläche seht, habt ihr eure eigene Rendertron-Instanz erfolgreich bereitgestellt. Schreibt euch eure Projekt-URL (YOUR_PROJECT_ID.appspot.com) auf, da ihr sie im nächsten Teil braucht.

Rendertron dem Server hinzufügen

Der Webserver verwendet express.js und Rendertron hat eine express.js-Middleware. Führt den folgenden Befehl im Verzeichnis der Datei server.js aus:

npm install --save rendertron-middleware

Durch diesen Befehl wird die Rendertron-Middleware aus npm installiert, sodass wir sie dem Server hinzufügen können:

const express = require('express');
const app = express();
const rendertron = require('rendertron-middleware');

Botliste konfigurieren

Rendertron ermittelt anhand der user-agent-HTTP-Kopfzeile, ob eine Anfrage von einem Bot oder von einem Browser eines Nutzers stammt. Hierfür steht eine sorgfältig geführte Liste von Bot-User-Agents zum Abgleich zur Verfügung. Der Googlebot ist standardmäßig nicht in dieser Liste enthalten, weil er JavaScript ausführen kann. Wenn Rendertron auch Googlebot-Anfragen rendern soll, müsst ihr den Googlebot der Liste der User-Agents hinzufügen:

const BOTS = rendertron.botUserAgents.concat('googlebot');
const BOT_UA_PATTERN = new RegExp(BOTS.join('|'), 'i');

Rendertron vergleicht die user-agent-Kopfzeile später mit diesem regulären Ausdruck.

Middleware hinzufügen

Damit Botanfragen an die Rendertron-Instanz gesendet werden können, müssen wir unserem express.js-Server die Middleware hinzufügen. Diese überprüft den User-Agent, von dem die Anfrage stammt, und leitet die Anfragen von bekannten Bots an die Rendertron-Instanz weiter. Fügt „server.js“ den folgenden Code hinzu und ersetzt dabei YOUR_PROJECT_ID durch eure Google Cloud Platform-Projekt-ID:

app.use(rendertron.makeMiddleware({
  proxyUrl: 'https://YOUR_PROJECT_ID.appspot.com/render',
  userAgentPattern: BOT_UA_PATTERN
}));

Bots, die die Beispiel-Website anfordern, erhalten die statischen HTML-Inhalte von Rendertron, sodass die Bots zum Anzeigen der Inhalte kein JavaScript benötigen.

Set-up testen

Wenn ihr herausfinden möchtet, ob das Rendertron-Set-up erfolgreich war, könnt ihr den Test auf Optimierung für Mobilgeräte noch einmal machen.

Der Test ergibt, dass die Seite für Mobilgeräte optimiert ist, und auf dem Screenshot sind jetzt alle fehlenden Katzen zu sehen.

Im Gegensatz zum ersten Test sind die Katzenbilder nun sichtbar. Auf dem HTML-Tab sehen wir sämtliche HTML-Inhalte, die vom JavaScript-Code generiert wurden, und stellen fest, dass wir dank Rendertron kein JavaScript mehr benötigen, um die Inhalte anzuzeigen.

Fazit

Ihr habt ein Set-up für dynamisches Rendering erstellt, ohne Änderungen an der Web-App vorzunehmen. So könnt ihr Crawlern eine statische HTML-Version der Web-App bereitstellen.