Cloudflare Workers und Mikro-Frontends: füreinander geschaffen
Dieser Beitrag ist auch verfügbar in English, 繁體中文 und 简体中文.

Um Entwicklern zu helfen, bessere Web zu erstellen, haben wir eine Fragmentes-Architektur erforscht und entwickelt, um Mikro-Frontends mit Cloudflare Workers zu erstellen, die blitzschnell ist, kostengünstig zu entwickeln und zu betreiben ist und sich auf die Bedürfnisse der größten Unternehmensteams skalieren lässt, ohne die Release-Geschwindigkeit oder zu beeinträchtigen Nutzererfahrung.
Hier geben wir einen technischen Überblick und ein Proof of Concept dieser Architektur.
Warum Mikro-Frontends?
Eine der Herausforderungen der modernen Frontend Web besteht darin, dass die Anwendungen größer und komplexer werden. Das gilt insbesondere für Web von Unternehmen, die E-Commerce, Banken, Versicherungen, Reisen und andere Branchen unterstützen, bei denen eine einheitliche Benutzeroberfläche den Zugriff auf eine Vielzahl von Funktionen ermöglicht. Bei solchen Projekten ist es üblich, dass mehrere Teams zusammenarbeiten, um eine einzige Web zu entwickeln. Diese monolithischen Web , die in der Regel mit JavaScript-Technologien wie React, Angular oder Vue erstellt wurden, umfassen Tausende oder sogar Millionen von Codezeilen.
Wenn eine monolithische JavaScript-Architektur mit Anwendungen dieser Größenordnung verwendet wird, ist das Ergebnis eine langsame und anfällige Nutzererfahrung mit niedrigen Lighthouse-Scores. Darüber hinaus haben zusammenarbeitende Entwicklungsteams oft Schwierigkeiten, ihre Teile der Anwendung aufrechtzuerhalten und weiterzuentwickeln , da ihr Los mit dem aller anderen Teams verbunden ist und sich die Fehler und technischen Schulden eines Teams oft auf alle auswirken.
Aufbauend auf Ideen aus dem Bereich Microservices hat die Frontend-Community begonnen, sich für Micro-Frontends einzusetzen, damit Teams ihre Funktionen unabhängig von anderen Teams entwickeln und bereitstellen können. Jedes Mikro-Frontend ist eine in sich geschlossene Mini-Anwendung, die unabhängig entwickelt und veröffentlicht werden kann und für die Wiedergabe eines Fragments der Seite verantwortlich ist. Die Anwendung kombiniert diese Fragmente dann so, dass sie sich aus der Sicht des Benutzers wie eine einzige Anwendung anfühlt.

Eine Anwendung, die aus mehreren Mikro-Frontends besteht
Fragmente könnten vertikale Anwendungsfunktionen wie Kontoverwaltung oder Kasse oder horizontale Funktionen wie Kopfzeile oder Navigationsleiste darstellen.
Clientseitige Mikro-Frontends
Ein gängiger Ansatz für Mikro-Frontends besteht darin, sich auf clientseitigen Code zu verlassen, um Fragmente nach und nach zu laden und zusammenzufügen (z. B. über Module Federal). Clientseitige Mikro-Frontend-Anwendungen leiden unter einer Reihe von Problemen.
Gemeinsamer Code muss entweder dupliziert oder als gemeinsam genutzte Bibliothek veröffentlicht werden. Gemeinsame Bibliotheken sind selbst problematisch. Es ist nicht möglich, ungenutzten Bibliothekscode zum Zeitpunkt der Erstellung zu verändern, was dazu führt, dass mehr Code als nötig in den Browser heruntergeladen wird und die Koordinierung zwischen den Teams, wenn gemeinsam genutzte Bibliotheken aktualisiert werden müssen, komplex und umständlich sein kann.
Außerdem muss die Containeranwendung der obersten Ebene gebootet werden, bevor die Mikro-Frontends überhaupt angefordert werden können, und sie müssen auch gebootet werden, bevor sie interaktiv werden. Wenn sie verschachtelt sind, kann es passieren, dass Sie einen Flut von Anfragen nach Mikro-Frontends erhalten, was zu weiteren Laufzeitverzögerungen führt.
Diese Probleme können dazu führen, dass die Anwendung für den Nutzer nur schleppend gestartet wird.
Serverseitiges Rendering könnte mit clientseitigen Micro-Frontends verwendet werden, um die Geschwindigkeit der Anzeige der Anwendung durch einen Browser zu verbessern, aber die Implementierung dieser Lösung kann die Komplexität von Entwicklung, Bereitstellung und Betrieb erheblich erhöhen. Darüber hinaus haben die meisten serverseitigen Rendering-Ansätze immer noch eine gewisse Verzögerung, bevor der Nutzer vollständig mit der Anwendung interagieren kann.
Die Bewältigung dieser Probleme war der Hauptgrund für die Suche nach einer Alternativlösung, die auf den verteilten, -latenz Eigenschaften von Cloudflare Workers beruht.
Mikro-Frontends auf Cloudflare Workers
Die Computing-Plattform Cloudflare Workers bietet eine hoch skalierbare und -latenz JavaScript-Ausführungsumgebung, die an über 275 Standorten weltweit verfügbar ist. Im Rahmen unserer Erkundungsphase haben wir Cloudflare Workers verwendet, um Mikro-Frontends von überall in unserem globalen Netzwerk zu hosten und zu rendern.
Fragmente-Architektur
In dieser Architektur besteht die Anwendung aus einem Baum von Fragmenten, die jeweils auf Cloudflare Workers bereitgestellt werden, die zusammenarbeiten, um die Gesamtantwort serverseitig zu rendern. Der Browser stellt eine Anfrage an ein Root-Fragment, das mit untergeordneten Fragmenten kommuniziert, um die endgültige Antwort zu generieren. Da Cloudflare Workers nahezu ohne Overhead miteinander kommunizieren können, können Anwendungen schnell serverseitig durch untergeordnete Fragmente gerendert werden, die alle parallel arbeiten, um ihren eigenen HTML-Code zu rendern und ihre Ergebnisse an das übergeordnete Fragment zu streamen, das sie zur endgültigen Antwort kombiniert der letzte Stream, der an den Browser geliefert wird.

Ein allgemeiner Überblick über die Architektur eines Fragments
Besuchen Sie die Cloud Bilder
Wir haben ein Beispiel für eine Cloud Library-Anwendung entwickelt, um zu zeigen, wie dies in der Praxis funktionieren kann. Er wird auf Cloudflare Workers unter https://cloud-galry.web-experiments.workers.dev/bereitgestellt.
Die Demoanwendung ist eine einfache gefilterte Sammlung von Cloud Bildern, die mithilfe unserer Fragments-Architektur erstellt wurde. Versuchen Sie, einen Tag in der Type-Ahead-Datei auszuwählen, um die in der Bibliothek aufgeführten Bilder zu filtern. Ändern Sie dann die Verzögerung im Strom der Cloud -Bilder, um zu sehen, wie die Type-Ahead-Filterung interaktiv sein kann, bevor die Seite fertig geladen ist.
Mehrere Cloudflare Workers
Die Anwendung besteht aus einem Baum von sechs zusammenarbeitenden, aber unabhängig voneinander einsetzbaren Cloudflare Workers, die jeweils ihr eigenes Fragment des Bildschirms rendern und ihre eigene clientseitige Logik sowie Assets wie CSS-Stylesheets und Bilder bereitstellen.

Überblick über die Architektur der Cloud Katalog App
Das Hauptfragment fungiert als Stammverzeichnis der Anwendung. Das Fragment header hat einen Schieberegler, mit dem Sie eine künstliche Verzögerung für die Anzeige von Katalogbildern konfigurieren können. Das Body-Fragment enthält das Filter-Fragment und die Galilee-Fragmente. Das Footer-Fragment schließlich zeigt nur statischen Inhalt.
Der vollständige Quellcode der Demo- App ist auf GitHub verfügbar.
Vorteile und Funktionen
Diese Architektur aus mehreren zusammenarbeitenden, serverseitig gerenderten Fragmenten, die auf Cloudflare Workers bereitgestellt werden, weist einige interessante Features auf.
Einkapselung
Fragmente sind vollständig gekapselt, sodass sie kontrollieren können, was sie besitzen und was sie anderen Fragmenten zur Verfügung stellen.
Fragmente können unabhängig voneinander entwickelt und bereitgestellt werden
Um eines der Fragmente zu aktualisieren, müssen Sie einfach dieses Fragment erneut bereitstellen. Die nächste Anfrage an die Hauptanwendung wird das neue Fragment verwenden. Außerdem können Fragmente ihre eigenen Assets (clientseitiges JavaScript, Bilder usw.) hosten, die über ihr übergeordnetes Fragment an den Browser gestreamt werden.
Es wird kein reiner Server-Code an den Browser gesendet
Neben der Reduzierung der Kosten für das Herunterladen von unnötigem Code in den Browser wird sicherheitsrelevanter Code, der nur für das serverseitige Rendern des Fragments benötigt wird, niemals anderen Fragmenten ausgesetzt und nicht in den Browser heruntergeladen. Außerdem können Features sicher hinter Feature-Flags in einem Fragment versteckt werden, was mehr Flexibilität bei der sicheren Einführung neuer Verhaltensweisen ermöglicht.
Zusammensetzbarkeit
Fragmente sind vollständig zusammensetzbar jedes Fragment kann andere Fragmente enthalten. Die daraus resultierende Baumstruktur gibt Ihnen mehr Flexibilität bei der Architektur und Bereitstellung Ihrer Anwendung. So können größere Projekte ihre Entwicklung und Bereitstellung skalieren. Außerdem könnte eine feinkörnige Kontrolle über die Zusammensetzung der Fragmente dazu führen, dass Fragmente, deren serverseitiges Rendering teuer ist, einzeln zwischengespeichert werden.
Fantastische Lighthouse-Ergebnisse
Das Streamen von servergerendertem HTML führt zu großartigen Nutzererfahrungen und Lighthouse -Bewertungen, was in der Praxis für zufriedenere Nutzer und höhere Konversionschancen für Ihr Unternehmen bedeutet.

Lighthouse-Bewertungen für die Cloud Galilee App
Jedes Fragment kann Anfragen an seine untergeordneten Fragmente parallelisieren und die resultierenden HTML-Streams in seine eigene, einzelne gestreamte, serverseitig gerenderte Antwort leiten. Dadurch kann nicht nur die Zeit zum Rendern der gesamten Seite verkürzt werden, sondern das Streamen jedes Fragments zum Browser reduziert die Zeit bis zum ersten Byte jedes Fragments.
Begeisterte Interaktivität
Eine der Stärken der Fragmente-Architektur besteht darin, dass Fragmente interaktiv werden können, während der Rest der Anwendung (einschließlich anderer Fragmente) noch an den Browser gestreamt wird.
In unserer Demo ist das filter-Fragment sofort interaktiv, sobald es gerendert wird, auch wenn der Bild-HTML für das catalog-Fragment noch geladen wird.
Damit man das besser sehen kann, haben wir oben im Header einen Schieberegler hinzugefügt, mit dem man eine Netzwerk- oder Datenbankverzögerung simulieren kann, die den HTML-Stream verlangsamt, der die Bilder in der Galilee rendert. Selbst wenn das Bilder-Fragment noch geladen wird, ist die Type-Ahead-Eingabe im Filter-Fragment bereits vollständig interaktiv.
Denken Sie nur an die Frustration, die diese begierige Interaktivität für Nutzer von Web mit unzuverlässiger Internet vermeiden könnte.
Was im Hintergrund geschieht
Wie bereits erwähnt, beruht diese Architektur auf der Bereitstellung dieser Anwendung als viele zusammenarbeitende Cloudflare Workers. Sehen wir uns einige Details an, wie dies in der Praxis funktioniert.
Wir haben mit verschiedenen Technologien experimentiert, und obwohl dieser Ansatz mit vielen Frontend-Bibliotheken und -Frameworks verwendet werden kann, haben wir festgestellt, dass das Qwik-Framework aufgrund seines HTML-first-Faktors und des geringen JavaScript-Overheads, der Probleme mit der Wasserversorgung vermeidet, besonders gut geeignet ist.
Implementierung eines Fragments
Jedes Fragment ist eine serverseitig gerenderte Qwik-Anwendung, die auf einem eigenen Cloudflare -Worker bereitgestellt wird. Das bedeutet, dass Sie sogar direkt zu diesen Fragmenten navigieren können. Das Header-Fragment wird zum Beispiel bereitgestellt auf https://cloud-galerie-header.web-experiments.workers.dev/.

Ein Screenshot des selbstgehosteten Header-Fragments
Das Header-Fragment wird mit Qwik als Header- Komponente definiert. Diese Komponente wird in einem Cloudflare Worker über einen fetch() -Handler gerendert:
export default {
fetch(request: Request, env: Record<string, unknown>): Promise<Response> {
return renderResponse(request, env, <Header />, manifest, "header");
},
};Die renderResponse() -Funktion ist ein Hilfsmittel, das wir geschrieben haben, um das Fragment serverseitig zu rendern und in den Hauptteil einer Antwort zu streamen, die wir vom fetch()- Handler zurückgeben.
Das Header-Fragment stellt seine eigenen JavaScript- und Bild-Assets von seinem Cloudflare -Worker bereit. Wir konfigurieren Wrangler so, dass es diese Assets zu Cloudflare und sie über unser Netzwerk bereitstellt.
Implementierung einer Fragmentkomposition
Fragmente, die untergeordnete Fragmente enthalten, haben zusätzliche Aufgaben:
- Fordern Sie untergeordnete Fragmente an und fügen Sie sie beim Rendern ihres eigenen HTML-Codes ein.
- Proxy-Anfragen für untergeordnete Fragment-Assets bis zum entsprechenden Fragment.
Injektion von untergeordneten Fragmenten
Die Position eines untergeordneten Fragments innerhalb seines übergeordneten Elements kann durch eine von uns entwickelte Fragmentplaceholder -Hilfskomponente angegeben werden. Zum Beispiel besteht das Fragment body aus den Fragmenten filter und catalog.
<div class="content">
<FragmentPlaceholder name="filter" />
<FragmentPlaceholder name="gallery" />
</div>Die Fragmentplaceholder- Komponente ist dafür verantwortlich, eine Anfrage für das Fragment zu stellen und den Fragment-Stream in den Ausgabe-Stream weiterzuleiten.
Proxy für Asset-Anfragen
Wie bereits erwähnt, können Fragmente ihre eigenen Assets hosten, insbesondere clientseitige JavaScript-Dateien. Wenn eine Anfrage nach einem Asset beim übergeordneten Fragment eintrifft, muss dieses wissen, welches untergeordnete Fragment die Anfrage erhalten soll.
In unserer Demo verwenden wir eine Konvention, wonach solchen Asset-Pfaden das Präfix /_fragment/<fragment-name> vorangestellt wird. Der Pfad zum Bild des Headerlogos lautet zum Beispiel /_fragment/header/cf-logo.png. Wir haben einen tryGetFragmentAsset() -Helfer entwickelt, der dem fetch()- Handler des übergeordneten Fragments hinzugefügt werden kann, um dies zu lösen:
export default {
async fetch(
request: Request,
env: Record<string, unknown>
): Promise<Response> {
// Proxy requests for assets hosted by a fragment.
const asset = await tryGetFragmentAsset(env, request);
if (asset !== null) {
return asset;
}
// Otherwise server-side render the template injecting child fragments.
return renderResponse(request, env, <Root />, manifest, "div");
},
};Fragmentierung von Asset-Pfaden
Wenn ein Fragment seine eigenen Assets hostet, müssen wir sicherstellen, dass der von ihm gerenderte HTML-Code das oben erwähnte spezielle Pfadpräfix _fragment/<fragment-name> verwendet, wenn es sich auf diese Assets bezieht. Wir haben eine Strategie dafür in den von uns entwickelten Helpern implementiert.
Die Fragmentplaceholder -Komponente fügt der Fragment-Anfrage einen `base`-searchParam hinzu, um ihr mitzuteilen, wie dieses Präfix lauten soll. Der renderResponse() -Helfer extrahiert dieses Präfix und stellt es dem serverseitigen Renderer von Qwik zur Verfügung. Dadurch wird sichergestellt, dass jede Anfrage für clientseitige JavaScript das richtige Präfix hat. Fragmente können einen von uns entwickelten Hook namens useFragmentRoot() anwenden. Dadurch können die Komponenten das Präfix aus einem FragmentContext-Kontext beziehen.
Da das Header-Fragment zum Beispiel die Logos von Cloudflare und Github als Assets hostet, muss es den Hook useFragmentRoot() aufrufen :
export const Header = component$(() => {
useStylesScoped$(HeaderCSS);
useFragmentRoot();
return (...);
});Auf den FragmentContext- Wert kann dann in Komponenten zugegriffen werden, die das Präfix anwenden müssen. Zum Beispiel die Bildkomponente :
export const Image = component$((props: Record<string, string | number>) => {
const { base } = useContext(FragmentContext);
return <img {...props} src={base + props.src} />;
});Dienstbindende Fragmente
Cloudflare Workers bieten einen Mechanismus namens Service Bindings , um Anfragen zwischen Cloudflare Workers effizient zu stellen und Netzwerkanfragen zu vermeiden. In der Demo verwenden wir diesen Mechanismus, um die Anfragen von übergeordneten Fragmenten an ihre untergeordneten Fragmente nahezu ohne Performancekosten zu stellen, während die Fragmente dennoch unabhängig bereitgestellt werden können.
Im Vergleich zu aktuellen Lösungen
Diese Fragmente-Architektur hat drei Eigenschaften, die sie von anderen aktuellen Lösungen unterscheiden.
Im Gegensatz zu Monolithen oder clientseitigen Mikro-Frontends werden Fragmente als unabhängige, serverseitig zusammengesetzte, serverseitig gerenderte Anwendungen entwickelt und bereitgestellt. Dies erhöht die Rendering-Geschwindigkeit erheblich und senkt die -latenz bei der Interaktion im Browser.
Im Gegensatz zu serverseitig gerenderten Micro-Frontends mit Node.js oder Cloud -Funktionen ist Cloudflare Workers eine global verteilte Computing-Plattform mit einem regionslosen Bereitstellungsmodell. Er hat eine unglaublich niedrige -latenz und einen Overhead für die Kommunikation zwischen den Fragmenten, der nahe bei Null liegt.
Im Gegensatz zu Lösungen, die auf Modul-Verbund basieren, ist das clientseitige JavaScript eines Fragments sehr spezifisch für das Fragment, das es unterstützt. Das bedeutet, dass sie klein genug ist, dass wir keinen Code für gemeinsam genutzte Bibliotheken benötigen, wodurch Probleme mit der Versionsabweichung und Koordinationsprobleme bei der Aktualisierung von gemeinsam genutzten Bibliotheken beseitigt werden.
Möglichkeiten der Zukunft
Diese Demo ist nur ein Proof-of-Concept, es gibt also noch Bereiche, die es zu untersuchen gilt. Hier sind einige der Funktionen, die wir in Zukunft gerne ausprobieren würden.
Zwischenspeicherung
Jedes Mikro-Frontend-Fragment kann unabhängig von den anderen im Cache zwischengespeichert werden, je nachdem, wie statisch sein Inhalt ist. Bei der Anforderung der vollständigen Seite müssen die Fragmente nur für Mikro-Frontends, die sich geändert haben, ein serverseitiges Rendering durchführen.

Eine Anwendung, bei der die Ausgabe einiger Fragmente zwischengespeichert wird
Mit Zwischenspeicherung pro Fragment können Sie die HTML-Antwort schneller an den Browser zurückschicken und vermeiden, dass Computing-Kosten durch unnötiges Neugerendern von Inhalten entstehen.
Fragment- Routing und clientseitige Navigation
Unsere Demoanwendung verwendete Mikro-Frontend-Fragmente, um eine einzelne Seite zusammenzusetzen. Wir könnten diesen Ansatz jedoch auch verwenden, um Page Routing zu implementieren. Beim serverseitigen Rendering könnte das Hauptfragment das entsprechende page-Fragment basierend auf der besuchten URL einfügen. Wenn clientseitig innerhalb der App navigiert wird, bleibt das Hauptfragment gleich, während sich das angezeigte Seitenfragment ändert.

Eine Anwendung, bei der jede Route an ein anderes Fragment delegiert wird
Dieser Ansatz kombiniert die besten Vorteile von serverseitigem und clientseitigem Routing mit der Leistungsfähigkeit von Fragmenten.
Verwendung anderer Frontend-Frameworks
Obwohl die Cloud Bilder-Anwendung Qwik verwendet, um alle Fragmente zu implementieren, ist es möglich, auch andere Frameworks zu verwenden. Wenn es wirklich notwendig ist, ist es sogar möglich, die Frameworks beliebig zu kombinieren.
Um gute Ergebnisse zu erzielen, sollte das Framework der Wahl serverseitig gerendert werden können und nur einen kleinen JavaScript-Footprint auf der Clientseite aufweisen. HTML-Streaming-Funktionen sind zwar nicht erforderlich, können aber die Performance großer Anwendungen erheblich verbessern.

Eine Anwendung, die verschiedene Frontend-Frameworks verwendet
Strategien für inkrementelle Migration
Die Einführung einer neuen Architektur, Rechenplattform und eines neuen Bereitstellungsmodells ist eine Menge auf einmal und für bestehende große Anwendungen unerschwinglich riskant und teuer. Um diese fragmentierte Architektur auch für bestehende Projekte verfügbar zu machen, ist eine schrittweise Einführungsstrategie unerlässlich.
Entwickler konnten dies testen, indem sie nur einen einzigen Teil der Benutzeroberfläche innerhalb ihrer veralteten Anwendung in ein Fragment migrieren und so die Integration mit minimalen Änderungen an der veralteten Anwendung durchführen. Mit der Zeit könnten dann weitere Teile der Anwendung verschoben werden, Fragment nacheinander.
Konvention statt Konfiguration
Wie Sie in der Cloud Bilder-Demoanwendung sehen können, erfordert die Einrichtung eines fragmentierten Mikro-Frontends eine Menge Konfigurationsaufwand. Viele dieser Konfigurationen sind sehr maschinell und könnten durch Konventionen und bessere Werkzeuge abstrahiert werden. Wenn wir dem produktivitätsorientierten Vorbild von Ruby on Rails folgen und dateisystembasierte Routing -Meta-Frameworks nutzen, könnten wir einen Großteil dieser Konfiguration verschwinden lassen.
Probieren Sie es selbst!
Es gibt noch so viel zu entdecken! Webanwendungen haben in den letzten Jahren einen langen Weg zurückgelegt, und ihr Wachstum ist kaum zu überschätzen. Herkömmliche Implementierungen von Mikro-Frontends hatten nur gemischten Erfolg, wenn es darum ging, Entwickler bei der Skalierung der Entwicklung und der Bereitstellung großer Anwendungen zu unterstützen. Cloudflare Workers eröffnet jedoch neue Möglichkeiten, die uns helfen können, viele der bestehenden Herausforderungen zu bewältigen und bessere Web zu entwickeln.
Dank des großzügigen Tarif -Tarifs von Cloudflare Workers können Sie sich den Demo-Code aus der Bildergalerie ansehen und ihn selbst bereitstellen.
Wenn all dies für Sie interessant klingt und Sie mit uns an der Verbesserung der Entwicklererfahrung für Cloudflare Workers arbeiten möchten, teilen wir Ihnen auch gerne mit, dass wir neue Mitarbeiter suchen!