Listen aller Art sind ein wichtiger Bestandteil von Webseiten. Sie sollen schnell geladen werden und einfach zu bedienen sein. Einmal geladene Listenelemente sollen lokal gespeichert und wiederverwendet werden. Interaktionen mit den Listenelementen sollen in Echtzeit zwischen verschiedenen Tabs/Fenstern synchronisiert werden.
Dieser Beitrag erklärt dir, wie du solch performante Listen mit Web-Komponenten und dem optimalsten Datenspeicher realisieren kannst.
Beispiel
Die folgenden zwei Listen zeigen die Verwendung von Web-Komponenten im Zusammenspiel mit einem Worker und ausgewählten Datenspeichern.
Voraussetzungen
Das Konzept der Web-Komponenten sollte dir bekannt sein. Ebenso solltest du wissen, wie ein Service-Worker funktioniert.
Die <article-list> ist ein benutzerdefiniertes Element. Die data-Parameter definieren das zu verwendende Template für die Liste (animation) und jenes für die darin enthaltenen Listenelemente (articleCard), sowie die Informationen der zu verwendenden Daten. Diese Informationen werden vom Javascript gelesen und verarbeitet.
Javascript
Die grunlegende Funktionalität haben wir in separate Klassen ausgelagert. Diese werden in der default.js geladen und initialisiert.
Ich sammle die Module im Unterordner modules und nenne die Dateien wie die Klasse, aber mit Kleinbuchstaben beginnend.
Service
Die Service-Klasse kümmert sich um die Registrierung des Service-Workers, das Anzeigen von Benachrichtigungen und die Kommunikation zwischen den Browser-Tabs.
In unserem Beispiel wird die Selektion der Items mit Listen in anderen Tabs synchronisiert. Wenn du also auf ein Produkt klickst, wird dieses grün. Und zwar in allen Listen in allen Tabs.
WorkerProxy
Die WorkerProxy-Klasse ist für die Kommunikation mit dem Worker verantwortlich. Der Worker wiederum interagiert mit der IndexedDB. Diese ist asynchron und kann die Performance deiner Anwendung beeinträchtigen. Um dies zu vermeiden, delegieren wir die Arbeit an einen Worker, der in einem separaten Thread läuft. Dieser kommuniziert über Nachrichten mit dem Haupt-Thread. Dadurch können wir Daten im Hintergrund laden, speichern und aktualisieren, ohne die Benutzeroberfläche zu beeinträchtigen.
DataStore
Die DataStore-Klasse verwaltet die verschiedenen Datenspeicher (IndexedDB, Local-Storage, Session-Storage, Server). Sie stellt Methoden zum Lesen und Schreiben von Daten bereit. In unserem Beispiel verwenden wir den DataStore, um die Listenelemente zu speichern und wiederzuverwenden. Dadurch müssen die Daten nicht bei jedem Laden der Seite neu vom Server geladen werden, was die Ladezeit erheblich verkürzt.
Icons
Die Icons-Klasse lädt und verwaltet SVG-Icons. Diese werden in den Listennavigation verwendet.
List
Die List-Klasse enthält die Kernfunktionalität, mit der wir uns in diesem Beitrag befassen. Sie liest die Daten aus dem DataStore und rendert die Animation. Das JavaScript dazu kannst du dir herunterladen.
Die List-Klasse definiert das Verhalten für ein benutzerdefiniertes HTML-Element namens <article-list>. Dieses Element ist dafür konzipiert, hochperformante und interaktive Listen von Artikeln darzustellen. Die Kernidee ist, Daten so effizient wie möglich zu laden, zwischenzuspeichern und Interaktionen über verschiedene Browser-Tabs hinweg zu synchronisieren.
Die List-Klasse, die von HTMLElement erbt, implementiert eine komplexe Ladelogik, die mehrere Optimierungstechniken kombiniert:
Lazy Loading der Komponente: Die gesamte Liste wird erst dann geladen und gerendert, wenn sie in den sichtbaren Bereich des Benutzers (den Viewport) scrollt. Dies wird mit einem IntersectionObserver im constructor erreicht.
Zentrale Datenverwaltung: Die Komponente verlässt sich auf ein globales dataStore-Modul, um Daten aus verschiedenen Quellen (IndexedDB, Server) abzurufen. Dies ermöglicht ein effizientes Caching.
Templating: Sowohl das Layout der Liste (z.B. ein Karussell mit Navigationspfeilen) als auch das Aussehen der einzelnen Listenelemente werden durch Vorlagen (Templates) definiert. Diese Templates (HTML und CSS) werden ebenfalls aus dem dataStore geladen.
Lazy Loading der Artikelinhalte: Nachdem das Grundgerüst der Liste erstellt wurde, werden die detaillierten Inhalte für jeden einzelnen Artikel (wie Titel, Bild-URL etc.) erst dann nachgeladen, wenn das jeweilige Listenelement in den Viewport scrollt. Dies geschieht ebenfalls mit einem IntersectionObserver.
Effiziente Datenabrufe: Anstatt für jeden Artikel eine separate Serveranfrage zu stellen, wird eine einzige Anfrage für alle Artikel der Liste gesendet, um die Latenz zu minimieren.
Echtzeit-Synchronisation: Klickt ein Benutzer auf einen Artikel, um ihn auszuwählen (oder die Auswahl aufzuheben), wird dieser Zustand in der IndexedDB gespeichert. Gleichzeitig wird über einen Service Worker eine Nachricht an alle anderen offenen Browser-Tabs gesendet, damit der Auswahl-Status dort ebenfalls in Echtzeit aktualisiert wird.
Serverabfragen
Jetzt fehlen uns nur noch ein paar Musterdaten. Die Dateien liegen im Ordner /includes/pools/. Auch diese kannst du dir herunterladen.