Neos Fusion - A Rendering Engine for the Modern Web

Neos Fusion - A Rendering Engine for the Modern Web

I presented the Neos Fusion Rendering Engine, which is the centerpiece of the Neos Content Application Platform (neos.io) and controls its rendering. I demonstrated how to use Fusion in combination Fluid for template-based layouts and how to create template-less layouts with server-side AFX components, similar to React.

E3a43e2c588568daf6e1c873449df6a0?s=128

Bastian Heist

August 30, 2018
Tweet

Transcript

  1. Fusion

  2. Bastian Heist • Full-Stack-Entwickler | Sandstorm • Neos Core Team

    Member seit 2017 • 7 Jahre SAP ERP Consultant | Merck @beheist @bastianheist
  3. Themen

  4. +

  5. Was ist Neos? ◦ 1

  6. Was ist eine Rendering Engine? ◦ 2

  7. Wie funktioniert Fusion? ◦ 3

  8. Die Zukunft denkt in Komponenten ◦ 4

  9. Wie baut man sowas? ◦ 5

  10. ◦ 1 Was ist Neos?

  11. Photo by Katherine Chase on Unsplash

  12. Photo by Vladimir Kudinov on Unsplash

  13. Editor Happiness

  14. None
  15. Developer Happiness

  16. Photo by Nathan Dumlao on Unsplash

  17. Content's First Choice

  18. The Neos Content Repository 18 /page /main /headline /text /sidebar

    /contact mysite
  19. The Neos Content Repository 19 /page /main /headline /text /sidebar

    /contact mysite unstructured BlogPost ContentCollection Headline Text ContentCollection ContactDetails
  20. A Node Type 20 Neos.Demo:ContactDetails: superTypes: Neos.Neos:Content: true Neos.NodeTypes.BaseMixins:ImageMixin: true

    ui: label: 'Contact Details' icon: 'icon-user' properties: title: type: string occupation: type: string email: type: string
  21. <demo>

  22. ◦ 2 Was ist eine Rendering Engine?

  23. Was ist eine Rendering Engine? 23 /page /main /headline /text

    /sidebar /contact mysite
  24. Was ist eine Rendering Engine? 24 /page /main /headline /text

    /sidebar /contact mysite Fusion
  25. Rendering Engine != Templating Language <div> {image} <h1>{title}</h1> <h2>{occupation}</h2> <p>{email}</p>

    </div>
  26. Rendering Engine > Templating Language • Welche Inhalte erscheinen auf

    der Seite? • In welcher Reihenfolge? • Welche Templates werden verwendet? • Wo kommen die Daten zur Anzeige her? • ... • => Eher eine Art View Controller! 26
  27. Architekturelle Einordnung 27 Front Controller Fusion Rendering Result <div> {image}

    <h1>{title}</h1> <h2>{occupation}</h2> <p>{email}</p> </div> Fluid /page /main /headline /text /sidebar /contact mysite Neos CR
  28. ◦ 3 Wie funktioniert Fusion?

  29. Fusion - Designziele • Wiederverwendbarkeit von Rendering-Elementen • Rendering entkoppelt

    von Content-Struktur • Ein Content - beliebig viele Rendering-Arten • Erweiterbarkeit durch Integratoren • Performance 29
  30. Fusion - Eigenschaften 30 Fusion ist eine hierarchische, prototypenbasierte Verarbeitungssprache.

  31. Prototypenbasiert 31 prototype(Neos.Demo:ContactDetails) < prototype(Neos.Neos:Content) { // Read some data

    from the Neos CR title = ${q(node).property('title')} occupation = ${q(node).property('occupation')} email = ${q(node).property('email')} // Decide which template to use for rendering templatePath = 'resource://Neos.Demo/Private/Templates/NodeTypes/ ContactDetails.html' }
  32. Prototypenbasiert 32 prototype(Neos.Demo:ContactDetails) < prototype(Neos.Neos:Content) { // Read some data

    from the Neos CR title = ${q(node).property('title')} occupation = ${q(node).property('occupation')} email = ${q(node).property('email')} // Decide which template to use for rendering templatePath = 'resource://Neos.Demo/Private/Templates/NodeTypes/ ContactDetails.html' }
  33. Prototypenbasiert 33 prototype(Neos.Demo:ContactDetails) < prototype(Neos.Neos:Content) { // Read some data

    from the Neos CR title = ${q(node).property('title')} occupation = ${q(node).property('occupation')} email = ${q(node).property('email')} // Decide which template to use for rendering templatePath = 'resource://Neos.Demo/Private/Templates/NodeTypes/ ContactDetails.html' }
  34. Prototypenbasiert 34 prototype(Neos.Demo:ContactDetails) < prototype(Neos.Neos:Content) { // Read some data

    from the Neos CR title = ${q(node).property('title')} occupation = ${q(node).property('occupation')} email = ${q(node).property('email')} // Decide which template to use for rendering templatePath = 'resource://Neos.Demo/Private/Templates/NodeTypes/ ContactDetails.html' }
  35. Hierarchisch 35 prototype(Neos.Demo:BlogPost) < prototype(Neos.Neos:Page) { body { mainMenu =

    Neos.Neos:Menu { ... } content { teaser = Neos.Neos:ContentCollection { ... } main = Neos.Neos:PrimaryContent { ... } } sidebar { contactDetails = Neos.Demo:ContactDetails } } }
  36. Hierarchisch 36 blogPost (Neos.Demo:BlogPost) body (Neos.Fusion:Array) mainMenu (Neos.Neos:Menu) content (Neos.Fusion:Array)

    teaser (Neos.Neos:ContentCollection) main (Neos.Neos:PrimaryContent) sidebar (Neos.Fusion:Template) contactDetails (Neos.Demo:ContactDetails)
  37. Verarbeitungssprache 37 contactDetails (Neos.Demo:ContactDetails) <div style="border: 1px solid darkblue; padding:

    1rem;"> <img alt="The author" src="http://.../penguin.jpg" width="800" height="530" /> <h3>Mr. Penguin</h3> <h4>Lead Fisherman</h4> <p> <strong>penguin@icecap.com</strong> </p> </div>
  38. Vorteile • Flexibel anpassbares Rendering • Nachträglich erweiterbar / veränderbar

    • Unabhängig von Struktur und Hierarchie des Contents • Unabhängig von verwendeter Templatesprache • Trennung von Aussehen und Verhalten (SOC) • Eingebauter, sehr leistungsfähiger Cache 38
  39. Warum nehmt ihr nicht einfach PHP!? • Hinter jedem Fusion-Prototyp

    steckt eine PHP-Klasse • Fusion ist exakt auf die Rendering-Domäne zugeschnitten • Durchgriff auf PHP für Domänenlogik ist jederzeit möglich • Wir haben die Möglichkeit, eigene Features unterzubringen • Caching • DSLs • ... 39
  40. ◦ 4 Die Zukunft denkt in Komponenten

  41. Probleme beim Rendering mit Templates • Oft sehr mächtige Templates

    • Vermischen verschiedener "Concerns" im Template: • HTML-Struktur, Design und Layout (Frontend) • Logik & Datengenerierung, z.B. mit View Helpern (Backend) • Datentransformation: z.B. Erstellung von Links, Skalierung von Bildern (Backend) • Definition der Editing Experience (Backend) 41
  42. Probleme beim Rendering mit Templates 42 <div style="border: 1px solid

    darkblue; padding: 1rem;"> <media:image image="{image}" alt="The author" width="800" height="530"/> <neos:contentElement.editable property="title" tag="h3"/> <neos:contentElement.editable property="occupation" tag="h4"/> <p> <neos:contentElement.editable property="email" tag="strong"/> </p> </div>
  43. Probleme beim Rendering mit Templates 43 <div style="border: 1px solid

    darkblue; padding: 1rem;"> <media:image image="{image}" alt="The author" width="800" height="530"/> <neos:contentElement.editable property="title" tag="h3"/> <neos:contentElement.editable property="occupation" tag="h4"/> <p> <neos:contentElement.editable property="email" tag="strong"/> </p> </div>
  44. Atomic.Fusion

  45. Prinzipien von Atomic.Fusion • Separation • Rendering / Aussehen
 -->

    Presentational Components • Datenakquisition und Transformation
 --> Mapping Components 45
  46. Prinzipien von Atomic.Fusion • Isolation • Seiteneffektfreie Presentational Components •

    Komponenten nehmen lediglich Daten entgegen und zeigen diese an • Kein Zulesen von Daten aus Templates - denn es gibt keine mehr! 46
  47. Prinzipien von Atomic.Fusion • Aggregation • Konsequentes Ausnutzen der Fusion-Hierarchie

    • Komplexe Komponenten werden aus einfacheren zusammengebaut 47
  48. Prinzipien von Atomic.Fusion • Colocation • Alle Bestandteile einer Komponente

    
 (Fusion, CSS, JavaScript, Assets) 
 liegen im gleichen Ordner 48
  49. Prinzipien von Atomic.Fusion 49

  50. Wie sieht das dann aus? 50 prototype(Neos.Demo:ContactDetails) < prototype(Neos.Neos:ContentComponent) {

    // Data Fetching & Editing Experience title = Neos.Neos:Editable { property = 'title' } occupation = Neos.Neos:Editable { property = 'occupation' } email = Neos.Neos:Editable { property = 'email' } image = ${q(node).property('image')} // Rendering renderer = Neos.Demo:ContactDetailsRenderer }
  51. Wie sieht das dann aus? 51 prototype(Neos.Demo:ContactDetails) < prototype(Neos.Neos:ContentComponent) {

    // Backend Concerns - Mapping Component title = Neos.Neos:Editable { property = 'title' } occupation = Neos.Neos:Editable { property = 'occupation' } email = Neos.Neos:Editable { property = 'email' } image = ${q(node).property('image')} // Rendering renderer = Neos.Demo:ContactDetailsRenderer }
  52. Wie sieht das dann aus? 52 prototype(Neos.Demo:ContactDetails) < prototype(Neos.Neos:ContentComponent) {

    // Data Fetching & Editing Experience title = Neos.Neos:Editable { property = 'title' } occupation = Neos.Neos:Editable { property = 'occupation' } email = Neos.Neos:Editable { property = 'email' } image = ${q(node).property('image')} // Frontend Concerns - Presentational Component renderer = Neos.Demo:ContactDetailsRenderer }
  53. Wie sieht das dann aus? 53 prototype(Neos.Demo:ContactDetailsRenderer) < prototype(Neos.Fusion:Tag) {

    tagName = 'div' content = Neos.Fusion:Array { imageUri = Neos.Demo:Image { image = ${props.image} } title = Neos.Fusion:Tag { tagName = 'h1' content = ${props.title} } occupation = Neos.Fusion:Tag { tagName = 'h2' content = ${props.occupation} } email = Neos.Fusion:Tag { tagName = 'p' content = Neos.Fusion:Tag { tagName = 'strong' content = ${props.email} } } } }
  54. AFX

  55. AFX 55 AFX is to Fusion what JSX is to

    React
  56. AFX 56 prototype(Neos.Demo:ContactDetailsRenderer) < prototype(Neos.Fusion:Value) { value = afx` <div

    style="border: 1px solid darkblue; padding: 1rem;"> <img src={props.imageUri} alt="The author"/> <h2>{props.title}</h2> <h4>{props.occupation}</h4> <p> <strong>{props.email}</strong> </p> </div> ` }
  57. AFX 57 prototype(Neos.Demo:ContactDetailsRenderer) < prototype(Neos.Fusion:Value) { value = afx` <div

    style="border: 1px solid darkblue; padding: 1rem;"> <img src={props.imageUri} alt="The author"/> <h2>{props.title}</h2> <h4>{props.occupation}</h4> <p> <strong>{props.email}</strong> </p> </div> ` }
  58. Super! ...ihr habt HTML nachgebaut.

  59. AFX - Composition mit Komponenten 59 prototype(Neos.Demo:ContactDetailsRenderer) < prototype(Neos.Fusion:Value) {

    value = afx` <div style="border: 1px solid darkblue; padding: 1rem;"> <img src={props.imageUri} alt="The author"/> <Neos.Demo:Headline>{props.title}</Neos.Demo:Headline> <Neos.Demo:SubHeadline>{props.occupation}</Neos.Demo:SubHeadline> <Neos.Demo:EmailAddress>{props.email}</Neos.Demo:EmailAddress> <Neos.Demo:BoxFooter style="bold" /> </div> ` }
  60. AFX - Composition mit Komponenten 60 prototype(Neos.Demo:Sidebar) { renderer =

    afx` <aside> <h1>Sidebar Header!</h1> <Neos.Demo:ContactDetails /> </aside> ` }
  61. AFX - PropTypes 61 prototype(Neos.Demo:ContactDetails) < prototype(Neos.Neos:ContentComponent) { @propTypes {

    title = ${PropTypes.string.isRequired} occupation = ${PropTypes.string} email = ${PropTypes.string} imageUri = ${PropTypes.string.isRequired} } renderer = afx` <div style="border: 1px solid darkblue; padding: 1rem;"> <img src={props.imageUri} alt="The author"/> <h2>{props.title}</h2> <h4 @if.exists={props.occupation}>{props.occupation}</h4> <p @if.exists={props.email}> <strong>{props.email}</strong> </p> </div> ` }
  62. AFX - Style Guide 62 prototype(Neos.Demo:ContactDetails) < prototype(Neos.Neos:ContentComponent) { @styleguide

    { title = 'Contact Details Example' props { title = 'Mister P' occupation = 'Greatest of Penguins' email = 'some@example.com' imageUri = 'http://.../penguin.jpg' } renderer = ... }
  63. AFX - Style Guide 63

  64. Atomic.Fusion & AFX • Denken in Komponenten und Atomic Design

    Principles • Deklarative API für Präsentations- und Mappingkomponenten • Klare Schnittstellen zwischen Frontend/Backend Concerns • Fluid als Dependency nicht mehr notwendig • Gleiche Performance- und Laufzeiteigenschaften wie 
 "normales" Fusion • Unmöglich, invalides HTML zu schreiben (Fusion Parser!) 64
  65. ◦ 5 Wie baut man sowas?

  66. Architekturelle Einordnung 66 Front Controller Fusion /page /main /headline /text

    /sidebar /contact mysite Rendering Result <div> {image} <h1>{title}</h1> <h2>{occupation}</h2> <p>{email}</p> </div> Fluid Neos CR
  67. Bestandteile von Fusion FusionView FusionService createRuntime() render() Parser parse() prototype(Neos.Demo:ContactDetails)

    < prototype(Neos.Neos:Conte // Data Fetching & Editing Experience title = Neos.Neos:Editable { property = 'title' } occupation = Neos.Neos:Editable { property = 'occupation' } email = Neos.Neos:Editable { property = 'email' } imageUri = Neos.Neos:ImageUri { asset = ${q(node).property('image')} } // Rendering
  68. Regexes!

  69. Bestandteile von Fusion Parser FusionView Runtime($AST) FusionService $AST __prototypeObjectName: 'Neos.Neos:ContentComponent'

    title: __objectType: 'Neos.Neos:Editable' __value: null __eelExpression: null property: title occupation: __objectType: 'Neos.Neos:Editable' __value: null __eelExpression: null property: occupation email: __objectType: 'Neos.Neos:Editable' __value: null __eelExpression: null property: email
  70. Bestandteile von Fusion FusionView Runtime render() FusionObject new() evaluateOrRetrieveFromCache()

  71. FusionObject = PHP-Klasse! prototype(Neos.Fusion:Tag) { @class = 'Neos\\Fusion\\FusionObjects\\TagImplementation' }

  72. Bestandteile von Fusion FusionObject Tag- Implementation Value- Implementation ...
 Implementation

  73. Bestandteile von Fusion FusionObject hierarchical evaluation Runtime $output FusionView $output

  74. Bestandteile von Fusion FusionView

  75. ◦ Done!

  76. ◦ Fragen? ?

  77. neos.io @beheist sandstorm.de