Card with the words "Reduce", "Reuse" and "Recycle"

Global gecachte Fragmente in Neos CMS

Kategorien:

Neos CMS und die Fusion-Rendering-DSL (Domain-spezifische Sprache) ermöglichen eine sehr detaillierte und zuverlässige Art des Cachens von Seiten, Inhaltselementen und sogar kleinsten Fragmenten und Werten. 

Wirklich hilfreich für die Rendering-Performance ist jedoch das cachen größerer Fragmente, die relativ viel Zeit zum Generieren benötigen und auf mehreren Seiten Ihrer Site identisch erscheinen.

Beispiele:

  • Hauptnavigation
  • Seitenfußzeile
  • Auflistung der neuesten Nachrichten
  • Kochrezept des Tages

So definieren Sie ein global gecachtes Element

Oft ist der aktuelle documentNode Teil der entryIdentifiers.

Das bedeutet, dass der gecachte Eintrag spezifisch für diesen Knoten / diese Seite ist.

Stattdessen würden Sie also einen statischen Bezeichner wie "page-footer" hinzufügen und für ein editierbares Element den Knoten, in dem der Inhalt bearbeitet wird.

Wenn nun zwei Seiten desselben Typs generiert werden, kann die zweite Seite den bereits gecachten "page-footer" wiederverwenden. Yay speedup!

Es gibt einen Haken

Wenn Sie das Caching-Verhalten mit einem Tool wie t3n/neos-debug untersuchen, sehen Sie vielleicht, dass der Footer auf Ihrer Homepage und die Footer auf Ihrer Kontaktseite unterschiedliche entryIdentifier-Hashes haben.

Der Grund dafür ist, dass der fusionPath des gerenderten Elements der letzte Bestandteil ist, der zu den verketteten Bezeichnern hinzugefügt wird, die bereits durch die GlobalCacheIdentifiers definiert sind und durch Ihre eigene Cache-Konfiguration festgelegt wurden.

Das heißt, wenn Ihre Homepage den Nodetyp My.Site:Homepage und Ihre Kontaktseite den Nodetyp My.Site:Page hat, wird der fusionPath unterschiedlich sein und daher ein anderer Hash generiert, was zu zwei separaten Cache-Einträgen führt.

Das ist notwendig, damit Fusion genau das rendert, was Sie wollen, aber leider nicht sehr offensichtlich.

Der Kontext

Kürzlich gab es eine Diskussion, die mich darauf aufmerksam machte, dass einige Projekte, an denen ich beteiligt bin, und sogar die Neos.Demo dies nicht korrekt tun. Das bedeutet, dass das Rendering der Seiten auf diesen Sites nicht so schnell ist, wie es sein könnte (wenn eine Seite nicht gecached ist).

Wie lösen wir das Problem also?

Indem wir das Element, das wir überall verfügbar haben wollen, in einen absoluten Rendering-Pfad rendern, unabhängig von jedem übergeordneten Knotentyp, der am Rendering beteiligt ist.

Hier ist also der Footer, die Sie vielleicht von der Neos.Demo-Seite kennen.

prototype(Neos.Demo:Document.Fragment.Menu.Meta) < prototype(Neos.Fusion:Component) {
    menuItems = Neos.Neos:MenuItems { ... }

    renderer = afx`...`

    @cache {
        mode = 'cached'
        entryIdentifier {
            static = 'metamenu'
			site = ${site}
        }
        entryTags {
            1 = ${Neos.Caching.nodeTypeTag('Neos.Neos:Document', site)}
        }
    }
}

Wie Sie sehen, gibt es einen statischen Eintragsbezeichner und die Site. Durch die Einbeziehung der Site können wir separate Cache-Einträge basierend auf dem Arbeitsbereich und der Dimension (z. B. Sprache) haben, in der sich der Site-Knoten befindet.

Ohne dies würden wir das gleiche Menü für jede Sprache erhalten.

Aber wie bereits erwähnt, würde das Meta-Menü nun einen anderen fusionPath haben, je nachdem, zu welchem Dokumentenknotentyp es gehört.

Um dies zu beheben, benennen wir den Prototyp wie folgt um:

prototype(Neos.Demo:Document.Fragment.Menu.Meta.Renderer)

Und wir rendern das Meta-Menü in einen absoluten Pfad:

metaMenu = Neos.Demo:Document.Fragment.Menu.Meta.Renderer

Toll! Jetzt würden wir einen Fehler bekommen, wenn wir das versuchen, weil wir in unserem Seitenprototyp versuchen, den alten Meta-Menü-Prototyp zu rendern.

Also definieren wir es noch einmal auf eine sehr einfache Weise:

prototype(Neos.Demo:Document.Fragment.Menu.Meta) < prototype(Neos.Fusion:Renderer) {
    renderPath = '/metaMenu'
}

Hurra! Unser Meta-Menü funktioniert wie zuvor und wenn Sie ein Debug-Tool verwenden, um die zwischengespeicherten Elemente zu inspizieren, werden Sie sehen, dass dasselbe zwischengespeicherte Meta-Menü nun auf allen Seitentypen verwendet wird, die es referenzieren.

Hinweis: Da die Site auch in den entryIdentifiers enthalten ist, berücksichtigt der gecachte Eintrag automatisch die aktuelle Dimension und andere Kontextteile des Site-Knotens.

Aber warum funktioniert es jetzt?

Das Meta-Menü hat jetzt einen statischen fusionPath, der nicht von anderen Prototypen abhängt, und über den Neos.Fusion:Renderer können wir diesen Pfad aufrufen und das Ergebnis dort einfügen, wo wir es brauchen.

Gibt es auch Nachteile?

Verwenden Sie dieses Konzept nicht für alles.

Zum Beispiel sollten Sie nicht versuchen, auf etwas Seitenspezifisches wie den documentNode im global gecachten Element zuzugreifen. Das würde die ganze Idee wieder kaputt machen und Sie sollten es lieber auf die "alte" Weise machen. Die gleichen Seitentypen würden immer noch das gleiche gecachte Element wiederverwenden.

Seien Sie vorsichtig, auf was Sie sonst noch in Ihrem Element zugreifen, das Sie zwischenspeichern wollen, und stellen Sie sicher, dass Sie es gut auf Seiteneffekte testen.

Gibt es weitere Tipps?

In der bereits erwähnten Diskussion zeigte Sebastian Flor einen Helfer, um ihre global gecachten Elemente in ihren Projekten zu "vereinheitlichen". Ich habe diesen genauen Code noch nicht ausprobiert, aber er könnte hilfreich sein.

Außerdem ist der oben gezeigte Code kurz genug, so dass Sie vielleicht nichts anderes brauchen. Es funktioniert für die meisten Elemente auf die gleiche Weise.

Ich habe die Hauptnavigation als global gecachtes Element erwähnt. Aber Sie werden sich vielleicht fragen, warum ich das tue, da die meisten Navigationen den aktuell ausgewählten Pfad oder das ausgewählte Element anzeigen.

Da das Rendern einer Navigation in vielen Projekten recht langsam werden kann, empfehle ich in den meisten Fällen, die aktiven Zustände einer Navigation über Javascript zu setzen.

Sie können durch die Elemente traversieren und ihre Klassen basierend auf der aktuellen Position ändern.

Dies ist eine gute Möglichkeit, die Anzahl der Varianten Ihrer Hauptnavigation auf eine pro Dimension zu reduzieren, anstatt so viele gerenderte Navigations-Cache-Einträge zu haben, wie Sie Seiten haben (multipliziert mit Ihren Dimensionen und möglicherweise Arbeitsbereichen.

Und der letzte Tipp: Verwenden Sie die neuesten Versionen von t3n/neos-debug, da ich den fusionPath visuell zu den entryIdentifiern für jedes Cache-Segment hinzugefügt habe.

Zusammenfassung

Global zwischengespeicherte Fragmente können die Rendering-Leistung Ihrer Site für nicht zwischengespeicherte Seiten erheblich steigern.

Wenn Ihre Komponenten zu komplex sind und seitenbezogene Teile verwenden, können Sie sie in kleinere Teile aufteilen. Versuchen Sie, eine große Komponente zu haben, die gecached werden kann, und haben Sie eine separate Cache-Konfiguration für den seitenbezogenen Teil. Oder laden Sie die notwendigen Informationen über JS.

Ich würde mich über Ihr Feedback freuen, vielleicht haben Sie sogar eine bessere Lösung für Fälle wie diesen :)