Ga naar inhoud

Mijn weg naar Web Components

Albert Skibinski •
In een sterrenstelsel hier ver, ver vandaan...

Ik verdiepte me in de huidige frontendframeworks en -libraries om beter te begrijpen waar we staan en hoe we hier zijn gekomen. Vooral de mogelijkheden van native web components (in vanilla JS) met webstandaarden interesseerden me, en hoe die zich verhouden tot deze frameworks en libraries. Ik besloot mijn aantekeningen in dit artikel te verzamelen. Misschien heeft iemand er iets aan.

Eerst wat terminologie.

Laat weten wat je ervan vindt!

Imperatief versus declaratief

Dit zijn algemene programmeerparadigma's, maar binnen frontendontwikkeling is een declaratieve weergave van de UI een standaardaanpak geworden. Daarmee kun je templates of views schrijven met databinding en de control flow apart houden. Het wordt ook beschreven als het maken van een UI als functie van de toestand.

Imperatief betekent juist dat je opeenvolgende stappen neemt om een taak uit te voeren. We kennen het allemaal van eenvoudige vanilla JS of jQuery voor DOM-manipulatie. Als je het over views hebt, gaat het over templates en hun engines. En daar bestaan verschillende smaken van.

Template-engines op basis van strings

Dit zijn engines zoals Mustache, die innerHTML gebruiken. Ze zijn het minst efficiënt, maar waren populair omdat DOM-manipulatie in oudere browsers traag was. innerHTML brengt ook beveiligingsrisico's mee. Het concept is wel DOM-onafhankelijk, wat een voordeel kan zijn, vooral bij server-side rendering (SSR). Overigens is Twig ook een template-engine op basis van strings, maar dan in PHP.

Template-engines op basis van de DOM

Voorbeelden hiervan zijn Angular, Alpine en Vue (Vue alleen bij gebruik van DOM-templates). In alle gevallen worden de templates geparseerd, wat problemen kan geven. Vue-ontwikkelaars waarschuwen bijvoorbeeld voor DOM-templates, omdat ze onvoorspelbaar kunnen zijn: de browser rendert de markup naar een DOM-template die vervolgens door het framework wordt gebruikt, en die twee kunnen dingen anders interpreteren. SSR is ook onmogelijk, omdat we op de server geen browser hebben.

JSX

JSX is geen template of engine. Het is declaratieve syntaxis waarmee de virtuele DOM wordt beschreven, nadat Babel die heeft omgezet naar JavaScript-objecten. JSX lijkt misschien op HTML-templates, maar dat is het niet. Het is zelfs niet specifiek voor React (Stencil gebruikt het ook). Je maakt er componenten mee die zowel markup als logica bevatten. Dat past beter bij de componentarchitectuur waarmee React begon.

Virtuele DOM

Geen template, maar een techniek om de DOM efficiënt bij te werken. De beste uitleg die ik vond, staat eigenlijk in een Svelte-blogartikel. Dat legt uit hoe React de DOM met de VDOM vergelijkt en de nodige wijzigingen aanbrengt.

Architectuur

De drie grote frameworks (Angular, React, Vue) zijn allemaal uitgekomen op het componentmodel en een MVVM-architectuur (Model-View-ViewModel).

  • Model (M): Het object dat de data en bedrijfslogica bevat.
  • ViewModel (VM): Componentcode die data aan de view bindt (en eventueel terug) en eenvoudige toestand beheert (presentatielogica).
  • View (V): Hoe het er in de browser uitziet. De DOM en de stijlen.

Let op: afhankelijk van de implementatie heb je misschien alleen een VVM-architectuur. Bijvoorbeeld bij een eenvoudige React-site die data rechtstreeks in het component ophaalt, zonder datalaag zoals GraphQL.

Components, Custom Elements en Web Components

De drie grote frameworks gebruiken componenten, maar geen ervan gebruikt standaard web components. Wat bedoelen we met native web components (of gewoon web components)? Die gebruiken drie hoofdtechnologieën, hoewel ik de ES-module-specificatie ook als vierde genoemd heb zien worden.

  • Custom Element
  • Shadow DOM
  • HTML-templates en slots

Je kunt ook alleen Custom Elements maken, zonder Shadow DOM en templates/slots; die zijn optioneel. Veel ontwikkelaars gebruiken alleen Custom Elements, omdat de Shadow DOM eigen uitdagingen heeft, vooral bij styling en SSR. Daarover hieronder meer.

lit-html + lit-element

De slimme mensen achter Polymer hebben twee libraries gemaakt. lit-html maakt het eenvoudiger om declaratieve templates te maken — UI als functie van data, zoals de frameworks dat doen — met minimale overhead. lit-element is in wezen een basisklasse waarmee je makkelijker Web Components maakt. Lit-html voelt als JSX, maar gebruikt intern tagged template literals om de DOM heel efficiënt bij te werken. Lit-element maakt standaard Custom Elements met Shadow DOM. Het lijkt dus een prettige manier om componenten op basis van webstandaarden te maken, met databinding, minimale overhead en veel flexibiliteit. In deze repository staan twee eenvoudige voorbeelden van lit-html en lit-element.

Stencil

Een andere tool is Stencil, van de slimme mensen bij Ionic. Het is niet echt een framework of library, maar een compiler die web components op basis van webstandaarden of complete PWA's maakt. Het gebruikt JSX en TypeScript en compileert naar native JavaScript. Daardoor is het heel geschikt voor integratie met andere frameworks of tools zoals Storybook, al kun je Stencil ook als zelfstandige componentlibrary gebruiken. Dit is mijn Stencil-speeltuin.

Svelte

Svelte is niet echt een tool om Web Components te maken, maar een alternatief met een andere aanpak dan de bestaande frameworks. Toch vind ik het de moeite van het noemen waard, omdat het overeenkomsten heeft met Stencil: beide zijn compilers die native JavaScript-componenten opleveren, bruikbaar door derden of zelfstandig. Maar Svelte gebruikt expliciet geen virtuele DOM. In plaats daarvan compileert het naar imperatieve code die de DOM bijwerkt, wat sneller kan zijn. Svelte maakt standaard geen Custom Elements, maar dat kan wel via een optie. TypeScript en betere testondersteuning staan op de roadmap.

Welke problemen lossen we eigenlijk op?

Waarom zouden we native web components verkiezen boven componenten uit frameworks? Interoperabiliteit en hergebruik op basis van standaarden. Stencil wil duidelijk de tool zijn om web components te maken die zonder afhankelijkheden in verschillende frameworks kunnen worden gebruikt.

Andere aandachtspunten

SSR en Shadow DOM

Je kunt web components met Shadow DOM niet declaratief maken zonder JavaScript: ze bestaan pas zodra we customElements.define() aanroepen.

Toegankelijkheid

Web components moeten met aandacht voor toegankelijkheid worden gebouwd, net als alle andere HTML.

Pfoe

Na (waarschijnlijk te veel) tijd te hebben besteed aan Web Components, verschillende frameworks, tools, libraries en aankomende voorstellen, heb ik het gevoel dat web components een heel eind zijn gekomen sinds ik er in 2015 voor het eerst mee speelde in Polymer 1.0.

Albert Skibinski

Over de auteur

  • Albert Skibinski is product engineer en co-founder van Jafix.
  • Ik schrijf over web development, lange fietstochten en lekker eten!