Stimulus, een JavaScript framework die uit je weg blijft

Auteur: Wiljan Slofstra

JavaScript frameworks zijn er in alle soorten en maten. De grap in de JavaScript community is dat elke dag zich een nieuw framework de kop opsteekt. Het is mooi dat nieuwe frameworks opstaan, omdat ze ons op nieuwe ideeën kunnen brengen. Frameworks kunnen ervoor zorgen dat er veel werk uit handen kan worden genomen van de ontwikkelaar.

Veel van jullie zullen bekend zijn met frameworks zoals Vue.JS, React, Angular en mogelijk Ember, Svelte, Polymer, ga zo maar door. Het probleem wat ik ondervind met veel van deze frameworks, is dat je hele applicatie door het framework gerendered wordt. Dit gebeurd standaard client-side (bij de gebruiker). Mocht je Node.JS als back-end gebruiken, dan kan je ervoor kiezen om je React of Vue.JS applicatie server-side te renderen. Dat is een nette oplossing.

Oplossingen in Laravel

Binnen OrangeTalent gebruiken we Laravel als back-end. Laravel is een prachtig framework, maar biedt niet de ruimte om een JavaScript applicatie server-side te renderen. Er zijn een paar oplossingen:

  • Door middel van een koppeling met een Node.JS applicatie kunnen de views alsnog server-side gerendered worden. In eerste instantie zal daarbij een vertraging ontstaan. Daarna wordt het lastiger om mee te werken; lokaal heb je meer opzet werk en op de server moet je meerdere systemen onderhouden.
  • Er zijn diensten die het mogelijk maken om JavaScript applicaties om te zetten naar statische HTML. Voor Google is dit een oplossing, maar voor de gebruiker moet de hele applicatie alsnog gerendered worden client-side. Daarbij vormt dit een probleem wanneer je dynamische content op pagina’s hebt.

Wat is er mis met client-side renderen?

Mijn mening is dat er teveel nadelen hangen aan het enkel renderen van je applicatie op de client-side:

Ten eerste is indexering van een JavaScript applicatie door Google een onzekerheid. Google geeft aan dat ze JavaScript applicaties uitvoeren en indexeren. Het is alleen onduidelijk tot op welke hoogte. In een talk op de Chrome Dev Summit 2019 hebben ze het over het renderen van een pagina tot deze ‘done’ is, maar wanneer is een pagina klaar? Een JavaScript applicatie kan nog bezig zijn met het ophalen van informatie van de back-end ver nadat Google je website heeft bestempeld als zijnde klaar. Dat deel wordt dus niet geïndexeerd. Het probleem kan zich overigens ook voordoen in een niet JavaScript framework applicatie.

Ten tweede is het renderen van een JavaScript applicatie over het algemeen zwaar voor je browser. Met server-side renderen doen we een deel van dit renderen al op de server-side, zodat de client-side sneller wordt. Als we niet server-side kunnen renderen komt alles op de client-side af. Zolang jij een evergreen browser (Chrome, Firefox, Opera, Edge) gebruikt op desktop is het renderen van een JavaScript framework geen enkel probleem. Sterker nog, ik denk dat je niet merkt dat het client-side gerendered is. Het probleem doet zich wel voor op (low-end) smartphones.

Bron: https://medium.com/@addyosmani/the-cost-of-javascript-in-2018-7d8950fbb5d4

Zoals je kan zien duurt het uitvoeren van JavaScript op een median smartphone ruim 4 keer zo lang als op een Macbook Pro.

Dus Stimulus?

Een JavaScript framework gebruiken om je hele applicatie client-side te renderen heeft wat haken en ogen zoals je hierboven hebt kunnen lezen. JavaScript frameworks hebben wel hun voordelen bij het ontwikkelen en we willen niet terug naar de tijd waar we overal losse jQuery scriptjes hebben.

Stimulus is een tussen oplossing. Stimulus is ontwikkeld door Basecamp en verkoopt zichzelf met:

A modest JavaScript framework for the HTML you already have.

Het biedt ‘bindings’ aan op je bestaande HTML (die in ons geval door Laravel wordt gerendered). Laten we kijken naar een minimaal voorbeeld:

<!-- view.blade.php —>
<div data-controller="collapse">
    <button type="button" data-action="click->collapse#onButtonClick">
        Klik mij!
    </button>
</div>
import { Controller } from 'stimulus';

class CollapseController extends Controller {
  onButtonClick() {
    alert('Je hebt op mij geklikt');
  }
}

export default CollapseController;

Alle logica in Stimulus wordt in controllers gestopt. Controllers zijn standaard ES2015 classes die de Stimulus controller extenden.

Voor de installatie verwijs ik je graag door naar de documentatie van Stimulus.

Basisbegrippen van Stimulus

Actions

Zoals je in ons minimale voorbeeld hebt kunnen zien kun je een data attribuut action toevoegen op een element. De data-action heeft een vast stramien:

data-action="eventtype->controller#method"

Let daarbij wel op dat een element met een data-action altijd binnen een div met de data-controller moet vallen die erbij hoort.

Targets

Targets hebben we nog niet laten zien in het voorbeeld, dus laten we daar nu naar kijken:

<div data-controller="my-component">
    <div data-target="my-component.output"></div>
</div>
import { Controller } from 'stimulus';

class MyComponentController extends Controller {
  connect() {
    this.outputTarget.textContent = 'Kijk mij';
  }
}

MyComponentController.targets = [
  'output’,
];

export default MyComponentController;

We hebben een div die een data-target heeft. In dit data-attribuut geven we net als bij actions aan om welke controller het gaat en hoe die moet heten.

Op onze class geven we aan welke targets er beschikbaar zijn. Deze targets kunnen we gebruiken binnen onze controller om bijvoorbeeld tekst te plaatsen of het element te manipuleren. Daarbij is het wel belangrijk om de naamgevingen van Stimulus te leren kennen. Je ziet dat onze target ‘output’ is genoemd, maar we gebruiken deze target, in de code als outputTarget. Waarom? Omdat er dan nog meer mogelijk is met een target:

this.outputTarget => Enkel element
this.outputTargets => Alle elementen met target
this.hasOutputTarget => Boolean of de target in je HTML voorkomt

State

Op een controller is het mogelijk om een simpele state te hebben door middel van data attributen:

onButtonClick() {
  this.data.set('index', this.data.get('index') + 1);
}

this.data geeft je toegang tot een aantal functies, has, get, en set.

Stimulus voor alles?

Dit was de basis van Stimulus, vrij simpel. Er is nog meer mogelijk, maar het lijkt me overbodig om de documentatie te vertalen, dus neem ook vooral hier een kijkje.

Stimulus biedt niet alles wat we kunnen verwachten van een framework als Vue.JS of React. Het is dus, zoals eerder benoemd, een tussen oplossing. Deze tussenoplossing is voor veel websites die we ontwikkelen ruim voldoende. Als je te maken hebt veel data stromen en dynamisch renderen van je state, dan heb je geen andere keus en zul je verder moeten kijken.

Voor interne applicaties maken we vaak de keuze voor Vue.JS. Voor front-facing applicaties zoals webshops, is het ook mogelijk om een combinatie te maken van Stimulus en Vue.JS. Je zou er bijvoorbeeld voor kunnen kiezen om alles met Stimulus te ontwikkelen, maar de winkelwagen in Vue.JS te bouwen.