7-Dec

React

Ha det bra, SPA

Det er leit å melde. Det er ikke lenger en god idé å rendre hele websiden din på klienten. En SPA er rett og slett ikke bra nok i 2022.

7 min read

·

By Kristofer Selbekk

·

December 7, 2022

Et historisk perspektiv

De siste 10 årene i frontend-verdenen har vært ganske… interessante. Skikkelig hektiske, faktisk. Vi har gått fra å ha nesten all logikk på serveren, via å ha noen kladder med jQuery-spaghetti her og der, til å lage både klient-monolitter og micro-frontends.

Vi gikk fra JSP til JSX, og besøkte både Backbone, Angular og Ember på veien. Vi var innom både CoffeeScript og Ruby on Rails, men endte opp med en ganske samla front rundt React, TypeScript — og kanskje litt Elm.

For det var single page applications — apper som kjørte på klientsiden — vi landet på som den best mulige løsningen. Vi laget en tykk klient, spurte noen APIer, og viste frem dataen. Grei skuring.

SPA er ikke bra… nok

Det føltes en stund som vi, på sett og vis, hadde løst frontend-utvikling. Ting funka. Men SPAer — på tross av sine mange fordeler — sliter fortsatt med en rekke ulemper vi rett og slett bare har levd med frem til nå.

En skilpadde, som er ganske treg.
Klient-side apper er trege, og det er ikke deres skyld

Det er fortsatt tregt

Den største ulempen med å ha så godt som all logikk i nettleseren, er at det krever en god del kode å implementere. Og når antall features i appen din går opp, vil tiden det tar før brukeren ser noe vettugt gå samme vei. Man må laste ned hundrevis av kilobytes med minimert JavaScript som må kjøres før noe skjer i det hele tatt!

Gode som vi er, har vi selvfølgelig kommet på teknikker for å delvis mitigere dette problemet, som å splitte koden i passende biter og hente det etterhvert som man trenger det. Dette fører riktignok til at du først må laste ned HTML-kode, så JavaScript-kode, som så genererer HTML-kode igjen. Og først da fyrer man av gårde eventuelle API-kall for å vise det faktiske innholdet!

Nettverk og vannfall

Et fenomen vi ofte ser er at selv om nettleseren potensielt får opp en slags ramme rundt nettsiden din ganske kjapt, vil det første som skjer være en rekke kall til diverse autentiseringstjenester og backender for å hente den dataen vil vise. Og mens vi venter på dem viser vi en spinner.

Andre ganger trenger man å gjøre et nettverks-kall før man gjør et annet. Som fører til spinnere som flytter på og formerer seg. Og før du vet det sitter man og teller spinnere som dukker opp og forsvinner, flytter på innhold og gjør siden din mer slitsom å se på.

Et screenshot av et typisk nettverksvannfall, der ett request trigger neste
Du kan nesten se laksen sprette oppover dette stryket av et vannfall. Bilde lånt fra remix.run

Det hjelper ikke at du fikk opp logoen din fort, når man fortsatt må vente 10 sekunder før ting er klart. Om noe så husker man hvem som ga deg en dårlig brukeropplevelse.

Vi mister SEO og forhåndsvisning

En annen konsekvens av å kjøre alt i nettleseren er at vi har mistet muligheten til å dynamisk sette innholdet i meta-taggene som brukes av søkemotorer og sosiale nettverk. Det betyr at innhold som før var enkelt å søke i og forhåndsvise nå kun kan vise frem en generisk beskrivelse av nettsiden din. Du kan heller ikke sette språket siden din vises på dynamisk, som vil hindre muligheten man har til å søke etter innhold på et gitt språk.

Når alt vi serverer til brukeren er en tom <div /> tag, er det ikke rart at det blir vanskeligere å indeksere innholdet ditt. Det er ikke alt innhold som skal indekseres, men det skader aldri å følge anbefalinger som fører til god indeksering på søketjenester.

Et screenshot av et side-preview - av Bekk.no. Der er det et bilde av to mennesker som jobber sammen, og en tekst som beskriver Bekk.
Previews på sider kan vi alltids få til, men uten å kjøre appen vår på serveren får vi aldri muligheten til å gi dynamiske previews. Alt er bare standard forside — og det er ikke alltid bra. Bilde lånt fra opengraph.ninja

Ingenting fungerer uten JavaScript

Et siste argument jeg vil nevne er at ingenting i en SPA fungerer om JavaScripten ikke er både slått på, lastet, og fungerer. Du får ikke opp innhold, du får ikke navigert deg rundt om på siden, og du får i alle fall ikke utført noen endringer! Det burde ringe en varselbjelle når det første man gjør i alle skjemaer er å kalle en funksjon som heter preventDefault.

Det betyr at man er beint nødt til å vente til alt er lastet før man i det hele tatt begynner å konsumere innholdet. Det finnes ingen grasiøs degradering av brukeropplevelsen — det er enten alt eller ingenting. Det er rett og slett ikke bra nok.

Statisk sidegenerering er ikke godt nok

Tankegodset jeg kommer med her er ikke nytt – og det har vært mange forskjellige forsøk på å løse disse utfordringene. Et av de som fikk god traction en periode var den såkalte Jam-stacken, der man brukte verktøy som Gatsby for å generere statiske HTML-filer, som så ble sendt til nettleseren, sammen med kode som gjorde dem interaktive etterhvert (såkalt hydrering).

Dette fungerte fint for statisk innhold, men med en gang man skulle være logget inn, måtte man rendre innholdet på klienten igjen.

Gatsby memen med Leo DiCaprio, captioned med "Lager side i Gatsby - krever innlogging"

Og ikke minst — jo mer innhold du hadde, jo lenger ble byggtiden din. Joda, man fikk etterhvert til inkrementelle bygg på visse infrastrukturer, men det er fortsatt langt fra godt nok. Og hvem liker vel vendor lockins?

Fremtiden er på serveren

Heldigvis har vi bedre måter å løse webutvikling på i 2022. For jeg mener at fremtiden av weben — den løses på serveren, og krydres på klienten.

Salt bae
Se for deg at serveren er biffen, saltet er klienten, og du er salt bae.

Remix og Next.js er to rammeverk som gjør nettopp dette for oss. De lar oss generere sidene våre på serveren, returnere ferdig HTML, som så blir gjort interaktiv (eller “blir hydrert”) av kode som lastes etter brukeren har fått opp innholdet sitt.

Tilpasset innhold uten spinners

Det at vi nå vet hvem som spør om en gitt side, gjør at vi kan generere innhold for den brukeren, eller for brukere med spesielle ønsker, uten å vise en eneste spinner. Og med React sin nyimplementerte støtte for å kunne strømme resultater tilbake til klienten litt etter litt, kan man fort begynne å oppnå raske lastetider også her.

Mange sider og API-kall kan også gjøres raskere med caching — både på klienten og på serveren. Og ved å sende de riktige caching-headerne når en viss side lastes, kan den mellomlagres på CDNer, og oppleves som vel så rask som en helt statisk HTML-fil.

Enda et screenshot fra nettverkstabben, denne gangen med alle requests parallelliserte
Når man baker hele stacken inn i samme rammeverk, kan man plutselig optimalisere nettverksgrafen veldig.

En annen fin konsekvens av å bruke et rammeverk som Remix er at man kan utlede databehov fra URLen man er på. Og når man vet mer om hva man trenger, kan man starte flere requests samtidig, som igjen gjør appen din kjappere.

Mye mer grasiøst 🩰

Remix er spesielt godt egnet for å lage løsninger som grasiøst degraderer seg om JavaScript skulle feile, eller ikke laste av en eller annen grunn. Den faller nemlig tilbake til de innebygde defaultene som nettleseren shipper med. Det er kanskje litt mer kludrete, men det fungerer godt i en knipe.

Det gjør også at du kan sende inn skjemaer eller navigere rundt på siden før JavaScripten er helt lastet inn – istedenfor å vente på at JavaScripten skal lastes. Det gir både deg og brukeren din et sikkerhetsnett, til og med når koden din kræsjer.

Et up-close bilde av en LP-spiller og stiften
Det kan hende vi nå er vitne til frontend sitt vinyl-øyeblikk. Det gamle er kult igjen. Bilde av Patrick Perkins på Unsplash

Høyere krav til både app og utvikler

Applikasjonene vi lager er mer avanserte, gjør mer, og stilles stadig høyere krav til. Man skal kunne bruke dem på mobilen i en tunnel, like enkelt som man kan skal kunne bruke dem på jobb. De skal være raske, tilgjengelige, delbare og gøyale.

Men vi har lært noe de siste 10 årene. Mulighetene for å lage gode løsninger som oppleves bedre for flere er definitivt der. Men å sende en megabyte med JavaScript til nettleseren din er ikke riktig tilnærming. Å kjøre samme koden på serveren, derimot, er det.

Vi må slutte å lage ting som kun kjører i nettleseren. Vi har bedre verktøy i verktøykassen, og vi kan skape bedre brukeropplevelser uten nevneverdig merarbeid. Og det høres ut som en god deal i min bok.