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.
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.
Vi spurte utviklerne på React Day Norway 2022 hvorfor de bruker React
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å:
#1: 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!
Se Selbekks favoritt-foredrag fra React Norway 2022-konferansen
#2: 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 ett 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å.
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.
#3: 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.
Sånn får Kron brukt samme kode på web og app med React Native for Web
#4: 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.
«Det burde ringe en varselbjelle når det første man gjør i alle skjemaer er å kalle en funksjon som heter preventDefault.»
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 etter hvert (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.
Og ikke minst — jo mer innhold du hadde, jo lenger ble byggtiden din. Joda, man fikk etter hvert til inkrementelle bygg på visse infrastrukturer, men det er fortsatt langt fra godt nok. Og hvem liker vel vendor lockins?
Mikael oppsummerer frontend-året: - Egentlig ikke så mye endring
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.
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.
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.
Amerikanske Evan lagde norsk Wordle-klon, for å lære seg norsk
Remix er 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.
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.