Komponenten sind großartig, nicht wahr? Sie sind diese wiederverwendbaren Wahrheitsquellen, die Sie verwenden können, um solide Frontends zu erstellen, ohne Code zu duplizieren.
Wissen Sie, was auch super cool ist? Headless Content Management! Headless Content-Management-Systeme (CMS) bieten eine Inhaltsbearbeitungserfahrung und geben diesen Inhalt in Form von Daten frei, die, nun ja, zu jeder API-konsumierenden Front-End-UI portiert werden können. Sie können Ihre Inhalte nach Belieben strukturieren (abhängig vom Produkt) und diese Inhalte in Ihre Front-End-Anwendungen ziehen.
Die gemeinsame Nutzung dieser beiden Dinge – eine verteilte CMS-Lösung mit komponentenbasierte Front-End-Anwendungen – ist ein Kernprinzip des Jamstack.
Aber während Komponenten und Headless-CMSs für sich genommen großartig sind, kann es schwierig sein, sie gut miteinander spielen zu lassen. Ich sage nicht, dass es schwierig ist, das eine mit dem anderen zu verbinden. In vielen Fällen ist es tatsächlich ziemlich schmerzfrei. Aber ein System von Komponenten zu schaffen, das wiederverwendbar und konsistent ist, und dieses System mit einer gut gestalteten CMS-Erfahrung auf dem gleichen Stand zu halten, ist schwer zu erreichen. Es ist diese Win-Win-Kombination aus der Freiheit, Inhalte frei zu schreiben, und der Möglichkeit, diese Inhalte in vorhersehbare Komponenten zu strukturieren, die Headless Content Management so attraktiv macht.
Parität zwischen einem CMS und Front-End-Komponenten erreichen
Mein liebstes Beispiel, das diese Komplexität verdeutlicht, ist eine einfache Komponente: ein Button. Nehmen wir an, wir arbeiten mit React, um Komponenten zu erstellen, und unser Button sieht so aus:
<Button to="/">Go Home</Button>
Im schönen Land React bedeutet das, dass die Komponente <Button> zwei Props (d. h. Eigenschaften, Attribute, Argumente usw.) hat – to und children. children ist ein React-Konzept, das den gesamten Inhalt zwischen den öffnenden und schließenden Tags enthält, was in diesem Fall „Go Home“ ist).
Wenn wir Inhalte in der Content-Editor-Oberfläche dazu befähigen möchten, Buttons zur Website hinzuzufügen, wünschen wir uns ein System, das es ihnen leicht macht, zu verstehen, wie ihre Aktionen im CMS das beeinflussen, was auf dem Bildschirm in der Front-End-App erscheint. Aber wir möchten auch, dass unsere Entwickler produktiv mit Komponenten-Eigenschaften arbeiten können, die für sie Sinn ergeben und innerhalb des Frameworks, mit dem sie arbeiten (d. h. im Beispiel React).
Wie machen wir das?
Wir könnten…
…Felder im CMS verwenden, die den Eigenschaften der Komponenten entsprechen, obwohl ich mit diesem Ansatz wenig Erfolg hatte. to und children ergeben für Content-Editoren, die versuchen, einen Button zu erstellen, wenig Sinn. Glauben Sie mir, ich habe es versucht. Ich habe es sowohl mit Anfängern als auch mit erfahrenen Redakteuren versucht. Ich habe Hilfetexte verwendet. Es spielt keine Rolle. Es ist verwirrend.
Sinnvoller ist die Verwendung von Begriffen, die Redakteure eher verstehen, wie label oder text für children und url für to.


Aber dann wären wir nicht mehr synchron mit unserem Code.
Oder was wäre, wenn wir…
…Attribute im CMS maskieren. Die meisten Headless CMS-Lösungen ermöglichen es Ihnen, einen anderen Wert für die Bezeichnung des Feldes zu haben als für den Namen, der bei der Bereitstellung von Inhalten über eine API verwendet wird.
Wir könnten unsere Felder mit Label und URL beschriften, aber children und to als Namen verwenden. Wir könnten. Aber wir sollten es wahrscheinlich nicht tun. Erinnern Sie sich, was Ian Malcolm gesagt hat?
Oberflächlich betrachtet erscheint das Maskieren von Attributen sinnvoll. Es ist eine Trennung der Zuständigkeiten. Die Redakteure sehen etwas, das sie glücklich und produktiv macht, und die Entwickler arbeiten mit den Namen, die für sie Sinn ergeben. Das gefällt mir, aber nur in der Theorie. In der Praxis verwirrt es Entwickler. Das Debuggen eines Problems eines Content-Editors erfordert oft das Durchsuchen zusätzlicher Ebenen (d. h. Zeit), um die Beziehung zwischen Bezeichnungen und Feldnamen zu finden.
Oder warum nicht …
…die Eigenschaften ändern. Wäre es nicht einfacher für Entwickler, flexibel zu sein? Sie entwerfen schließlich das System.
Ja, das stimmt. Aber wenn Sie diese Regel ausschließlich befolgen, werden Sie unweigerlich auf ein Problem stoßen. Wahrscheinlich werden Sie gegen das Framework kämpfen oder die Props werden sich einfach komisch anfühlen.
In unserem Beispiel funktionieren die Verwendung von label und url als Props für einen Button für Daten, die aus dem CMS stammen, einwandfrei. Aber das bedeutet auch, dass jedes Mal, wenn unsere Entwickler einen Button im Code verwenden möchten, es so aussieht:
<Button label="Go Home" url="/" />
Das mag oberflächlich in Ordnung erscheinen, aber es schränkt die Leistungsfähigkeit des Buttons erheblich ein. Nehmen wir an, ich möchte eine andere Funktion unterstützen, wie das Hinzufügen eines Icons innerhalb des Labels. Ich werde zusätzliche Logik oder eine weitere Eigenschaft dafür benötigen. Wenn ich stattdessen den children-Ansatz von React verwendet hätte, hätte es einfach funktioniert (wahrscheinlich nach einiger benutzerdefinierter Stilunterstützung).
Okay, also… was tun wir?
Wir stellen Transformer vor
Der beste Ansatz, den ich gefunden habe, ist, die Erfahrungen von Redakteuren und Entwicklern separat zu optimieren. Gestalten Sie eine CMS-Erfahrung, die auf die Redakteure zugeschnitten ist. Erstellen Sie eine Codebasis, die für Entwickler leicht zu navigieren, zu verstehen und zu erweitern ist.
Das Ergebnis ist, dass die beiden Erfahrungen nicht im Gleichstand miteinander stehen werden. Wir benötigen einige Dienstprogramme, um die Daten von der CMS-Struktur in etwas zu transformieren, das vom Front-End verwendet werden kann, unabhängig vom verwendeten Framework und der verwendeten Tooling.
Ich nenne diese Dienstprogramme Transformer. (Bin ich nicht gut im Benennen von Dingen!?) Transformer sind dafür verantwortlich, Daten von Ihrem CMS zu konsumieren und sie in eine Form zu transformieren, die leicht von Ihren Komponenten konsumiert werden kann.

Während ich festgestellt habe, dass die Transformation von Daten der reibungsloseste Weg ist, um großartige Erfahrungen sowohl im CMS als auch in der Codebasis zu erzielen, habe ich keine offensichtliche Lösung dafür, wie (oder vielleicht wo) diese Transformationen stattfinden sollen. Ich habe drei verschiedene Ansätze verwendet, die alle ihre Vor- und Nachteile haben. Schauen wir sie uns an.
1. Neben Komponenten
Ein Ansatz ist, Transformer direkt neben den Komponenten zu platzieren, denen sie dienen. Dies ist der Ansatz, den ich typischerweise bei der Organisation von komponentenbasierte Projekten verfolge – verwandte Dateien nahe beieinander zu halten.

Das bedeutet, dass ich oft ein Verzeichnis für jede Komponente mit einer vorhersehbaren Dateistruktur habe. Die index.js fungiert als Controller für die Komponente. Sie ist dafür verantwortlich, alle anderen relevanten Dateien zu importieren und zu exportieren. Das macht es trivial, die Komponente mit Logik-basiertem Verhalten zu umschließen. Mit anderen Worten, sie könnte Eigenschaften der Komponente transformieren, bevor sie gerendert wird. Hier ist ein Beispiel dafür, wie das für unseren Button aussehen könnte:
import React from "react"
import Component from "./component"
import transform from "./transformer"
const Button = props => <Component {...transform(props)} />
export default Button
Die Datei transform.js könnte so aussehen:
export default input => {
return {
...input,
children: input.children || input.label,
to: input.to || input.url
}
}
In diesem Beispiel, wenn to und children Eigenschaften waren, die an die Komponente gesendet wurden, funktioniert es einwandfrei! Aber wenn stattdessen label und url verwendet wurden, werden sie zu children und to transformiert. Das bedeutet, dass die Komponente <Button> (component.js) sich nur darum kümmern muss, children und to zu verwenden.
const Button = ({ children, to }) => <a href={to}>{children}</a>
Ich persönlich liebe diesen Ansatz. Er hält die Logik eng mit der Komponente gekoppelt. Der größte Nachteil, den ich bisher gefunden habe, ist, dass es sich um eine große Anzahl von Dateien und Transformationen handelt, während der gesamte Datensatz für eine bestimmte Seite früher im Stack transformiert werden könnte, was wäre…
2. Am oberen Ende des Trichters
Die Daten müssen über einen Mechanismus in die Anwendung geladen werden. Entwickler nutzen diesen Mechanismus, um so viele Daten wie möglich für die aktuelle Seite oder Ansicht abzurufen. Oft gilt: Je weniger Abfragen/Anfragen eine Seite benötigt, desto besser ist ihre Leistung.
Mit anderen Worten, dieser Mechanismus existiert oft am oberen Ende des Trichters (oder Stacks), im Gegensatz dazu, dass jede Komponente ihre eigenen Daten dynamisch abruft. (Wenn das notwendig ist, verwende ich Adapter.)

Der Mechanismus, der die Seitendaten abruft, könnte auch für die Transformation aller Daten für die gegebene Seite verantwortlich sein, bevor sie irgendeine ihrer Komponenten rendert.
Theoretisch ist dies ein besserer Ansatz als der erste. Er reduziert die Arbeitslast des Browsers, was die Leistung des Front-Ends verbessern sollte. Das bedeutet, dass der Server mehr Arbeit leisten muss, aber das ist oft eine bessere Wahl.
In der Praxis ist das jedoch eine Menge Arbeit. Datenstrukturen können groß, komplex und miteinander verwoben sein. Es kann eine Menge Arbeit dauern, alles im oberen Teil des Trichters in das richtige Format zu transformieren und die transformierten Daten dann an die Komponenten weiterzugeben. Außerdem ist es aufgrund der potenziellen Komplexität und Variation des riesigen Daten-Blobs, der am oberen Ende des Stacks abgerufen wird, schwieriger zu testen. Beim ersten Ansatz ist das Testen der Transformer-Logik für den Button trivial. Bei diesem Ansatz müssten Sie die Transformation von Button-Daten überall berücksichtigen, wo sie im abgerufenen Datenobjekt erscheinen könnten.
Aber wenn Sie es schaffen, ist dies im Allgemeinen der bessere Ansatz.
3. Die Middleman-Engine
Der dritte und letzte (und magische) Ansatz ist, all diese Arbeit woanders zu erledigen. In diesem Fall könnten wir eine Engine (d. h. eine kleine Anwendung) bauen, die die Transformationen für uns durchführt und dann die Inhalte für die Anwendung verfügbar macht.

Dies ist wahrscheinlich noch mehr Arbeit als der zweite Ansatz. Und es fallen zusätzliche Kosten und Wartungsaufwand für den Betrieb einer zusätzlichen Anwendung an, was mehr Anstrengung erfordert, um sicherzustellen, dass sie absolut stabil ist.
Der Hauptvorteil dieses Ansatzes ist, dass wir ihn als abstrahierte Engine bauen könnten. Mit anderen Worten, jedes Mal, wenn wir Daten in eine Front-End-Anwendung einbinden, durchläuft sie diese Middleman-Engine. Das bedeutet, wenn wir zwei Projekte haben, die dasselbe CMS oder dieselbe Datenquelle verwenden, ist die Arbeit für das zweite Projekt erheblich reduziert.
Wenn Sie heute nichts davon tun und damit beginnen möchten, rate ich Ihnen, diese Ansätze als Trittsteine zu betrachten. Sie wachsen in Komplexität, Wartung und Leistungsfähigkeit mit der wachsenden Anwendung. Beginnen Sie mit dem ersten Ansatz und sehen Sie, wie weit Sie damit kommen. Dann, wenn Sie das Gefühl haben, dass Sie von einem Sprung zum zweiten profitieren könnten, tun Sie es! Und wenn Sie sich wagemutig fühlen, gehen Sie für den dritten!
Am Ende ist das Wichtigste, eine Erfahrung zu schaffen, die sowohl Ihre Redakteure als auch Ihre Entwickler verstehen und genießen. Wenn Sie das schaffen, haben Sie gewonnen!
Ich sehe die Vorzüge der Middleman-Engine, aber ich glaube nicht, dass ich diesen Aufwand betreiben könnte. Ich habe die Lösung am oberen Ende des Trichters mit ziemlich gutem Erfolg umgesetzt. Sie ermöglicht es Ihnen auch, mit dem Content-Modell im CMS auf eine Weise zu spielen, bei der Sie einen einzigen Content-Typ haben könnten, aber basierend auf spezifischen Eigenschaften verschiedene Komponenten rendern könnten. Leider ist dies meist eine Reaktion auf einen Dienst wie Contentful, der die Anzahl der von Ihnen erstellbaren Content-Typen begrenzt, aber gut funktioniert, wenn Sie möglicherweise einen Content-Typ mit gruppierten Bildern haben und zwischen einer Raster- oder Slider-Darstellung davon wechseln möchten. Diese könnten als separate Komponenten im Front-End implementiert werden.
Oder Sie könnten einfach die entwicklerfreundlichen Namen verwenden, aber die Feldbezeichnungen so ändern, dass sie redaktionsfreundlicher sind. Das macht die Dinge konsistent und einfach und bietet den Redakteuren eine bessere Erfahrung.