Avveiningene til CSS-in-JS

Foto av Artem Bali

Nylig skrev jeg en oversikt over høyere nivå av CSS-in-JS, mest for å snakke om problemene denne tilnærmingen prøver å løse. Bibliotekforfattere bruker sjelden tid på å beskrive avveiningene av løsningen. Noen ganger er det fordi de er for partiske, og noen ganger vet de ikke hvordan brukerne bruker verktøyet. Så dette er et forsøk på å beskrive avveiningene jeg har sett så langt. Jeg synes det er viktig å nevne at jeg er forfatteren av JSS, så jeg bør anses som partisk.

Sosial påvirkning

Det er et lag mennesker som jobber på nettplattformen og ikke kjenner noe JavaScript. Disse menneskene får betalt for å skrive HTML og CSS. CSS-in-JS har hatt stor innvirkning på utviklernes arbeidsflyt. En virkelig transformativ endring kan aldri gjøres uten at noen mennesker blir etterlatt. Jeg vet ikke om CSS-in-JS må være den eneste måten, men masseadopsjonen er et tydelig tegn på problemer med å bruke CSS i moderne applikasjoner.

En stor del av problemet er vår manglende evne til å kommunisere nøyaktig brukssaker der CSS-in-JS lyser og hvordan vi kan bruke det riktig til en oppgave. Mange CSS-in-JS-entusiaster har lyktes med å markedsføre teknologien, men ikke mange kritikere snakket om avveiningene på en konstruktiv måte, uten å ta billige svinger på verktøyene. Som et resultat la vi mange avveininger skjult og gjorde ikke en sterk innsats for å gi forklaringen og løsningene.

CSS-in-JS er et forsøk på å gjøre saker med kompleks bruk lettere å håndtere, så ikke skyv den dit den ikke er nødvendig!

Kjøretidskostnad

Når CSS genereres fra JavaScript under kjøring, i nettleseren, er det en iboende overhead. Kostnader for kjøretid varierer fra bibliotek til bibliotek. Dette er en god generisk referanseindeks, men sørg for å lage dine egne tester. Store forskjeller ved kjøretid vises avhengig av behovet for å ha en fullstendig CSS-parsing av malstrenger, mengde optimaliseringer, detaljerte implementeringsdetaljer for dynamiske stiler, hash-algoritme og kostnadsrammer for integrering av rammer. *

I tillegg til den potensielle driftskostnaden for runtime, må du ta i betraktning fire forskjellige pakningsstrategier, fordi noen CSS-in-JS-biblioteker støtter flere strategier, og det er opp til brukeren å bruke dem. *

Strategi 1: Kun kjøretidgenerering

Runtime CSS-generasjon er en teknikk som genererer en CSS-streng i JavaScript og deretter injiserer den strengen med en stilkode i dokumentet. Denne teknikken produserer et stilark, IKKE inline stiler.

Avveining av generasjon av runtime er manglende evne til å gi stylet innhold på et tidlig tidspunkt, ettersom dokumentet begynner å lastes. Denne tilnærmingen passer vanligvis for applikasjoner uten innhold som kan være nyttig umiddelbart. Vanligvis krever slike applikasjoner brukerinteraksjoner før de virkelig kan bli nyttige for en bruker. Ofte fungerer slike applikasjoner med innhold som er så dynamisk at det blir utdatert så snart du laster det inn, så du må etablere en oppdateringsrørledning tidlig på for eksempel Twitter. I tillegg, når en bruker er logget inn, er det ikke nødvendig å oppgi HTML for SEO.

Hvis interaksjonen krever JavaScript, må pakken lastes før appen er klar. For eksempel kan du vise innholdet i en standardkanal når du legger inn Slack i dokumentet, men det er sannsynlig at brukeren vil endre kanalen rett etter det. Så hvis du lastet det første innholdet bare for å kaste dem bort umiddelbart.

Opplevd ytelse av slike applikasjoner kan forbedres med plassholdere og andre triks for å la applikasjonen føles mer øyeblikkelig enn den faktisk er. Slike applikasjoner er vanligvis data tunge uansett, så de vil ikke være nyttige like raskt som en artikkel.

Strategi 2: Runtime-generasjon med Critical CSS

Kritisk CSS er den minimale mengden CSS som kreves for å style siden i den opprinnelige tilstanden. Den er gjengitt ved hjelp av en stilkode i hodet på dokumentet. Denne teknikken er mye brukt med og uten CSS-in-JS. I begge tilfeller er det sannsynlig at du dobbeltbelaster CSS-reglene, en gang som en del av Critical CSS og en gang som del av JavaScript- eller CSS-pakken. Størrelsen på Critical CSS kan være ganske stor avhengig av mengden på innholdet. Vanligvis vil ikke dokumentet bli bufret.

Uten kritisk CSS, må en statisk innholdstung enkel sidesøknad med runtime CSS-in-JS vise plassholdere i stedet for innhold. Dette er dårlig fordi det kunne vært nyttig for en bruker mye tidligere, og forbedret tilgjengeligheten på avanserte enheter og for tilkoblinger med lav båndbredde.

Med kritisk CSS, kan runtime CSS-generasjon gjøres på et senere tidspunkt, uten å blokkere brukergrensesnittet i den innledende fasen. Vær imidlertid advart om at avanserte mobile enheter, som er omtrent 5 år gamle, kan CSS-generasjon fra JavaScript ha en negativ innvirkning på ytelsen. Det avhenger sterkt av mengden CSS som blir generert og biblioteket som brukes, så det kan ikke generaliseres.

Utvekslingen av denne strategien er kostnadene ved kritisk CSS-utvinning og kostnadene for runtime CSS-generering.

Strategi 3: Bare utvinning av byggetid

Denne strategien er den vanlige på nettet uten CSS-in-JS. Noen CSS-in-JS-biblioteker lar deg trekke ut statisk CSS ved byggetid. * I dette tilfellet er det ikke noe driftskostnad involvert, CSS blir gjengitt på siden ved hjelp av en lenketagg. Kostnadene for CSS-generasjonen betales en gang i forveien.

Det er to store avveininger her:

  1. Du kan ikke bruke noen av de dynamiske API-ene CSS-in-JS-tilbudene under kjøring, fordi du ikke har tilgang til staten. Ofte kan du fremdeles ikke bruke CSS-egendefinerte egenskaper, fordi de ikke støttes i alle nettlesere og ikke kan polyfylles på byggetid av natur. I dette tilfellet må du gjøre løsninger for dynamisk tema og statsbasert styling. *
  2. Uten kritisk CSS og med en tom cache, blokkerer du den første malingen til CSS-pakken blir lastet. Et lenkeelement i hodet på dokumentet blokkerer gjengivelsen av HTML.
  3. Ikke-deterministisk spesifisitet med sidebasert pakkeinndeling i applikasjoner på en side. *

Strategi 4: Byggetidsekstraksjon med Critical CSS

Denne strategien er heller ikke unik for CSS-in-JS. Full statisk ekstraksjon med kritisk CSS gir den beste ytelsen når du arbeider med en mer statisk applikasjon. Denne tilnærmingen har fremdeles de nevnte avveiningene av en statisk CSS, bortsett fra at koden for blokkeringslink kan flyttes til bunnen av dokumentet.

Det er fire hovedstrategier for CSS-gjengivelse. Bare to av dem er spesifikke for CSS-in-JS, og ingen av dem gjelder alle biblioteker.

tilgjengelighet

CSS-in-JS kan redusere tilgjengeligheten når den brukes på feil måte. Dette vil skje når et stort sett statisk innholdsside implementeres uten kritisk CSS-ekstraksjon, slik at HTML ikke kan males før JavaScript-pakken lastes inn og evalueres. Dette kan også skje når en enorm CSS-fil blir gjengitt ved hjelp av en blokkeringslenke-kode i hodet på dokumentet, som er det mest populære aktuelle problemet med tradisjonell innebygging og ikke spesifikt for CSS-in-JS.

Utviklere må ta ansvar for tilgjengeligheten. Det er fremdeles en sterk misforstått ide om at en ustabil internettforbindelse er et problem for økonomisk svake land. Vi har en tendens til å glemme at vi har tilkoblingsproblemer hver eneste dag når vi kommer inn i et underjordisk jernbanesystem eller et stort bygg. En stabil kabelfri mobilforbindelse er en myte. Det er ikke engang lett å ha en stabil WiFi-tilkobling, for eksempel kan et 2,4 GHz WI-FI-nettverk få forstyrrelser fra en mikrobølgeovn!

Kostnaden for kritisk CSS med serversiden rendering

For å få kritisk CSS-ekstraksjon for CSS-in-JS, trenger vi SSR. SSR er en prosess for å generere den endelige HTML for en gitt tilstand av en applikasjon på serveren. Det kan faktisk være en ganske kompleks og kostbar prosess. Det krever en viss CPU-syklus på serveren for hver HTTP-forespørsel.

CSS-in-JS utnytter vanligvis det faktum at den er koblet til HTML-gjengivelsesrørledningen. * Den vet hva HTML ble gjengitt og hvilken CSS den trenger, slik at den kan produsere den absolutte minimale mengden av den. Kritisk CSS legger til ekstra overhead til HTML-gjengivelse på serveren fordi CSS også må kompileres til en endelig CSS-streng. I noen scenarier er det vanskelig eller til og med umulig å cache på serveren.

Rendering black box

Du må være klar over hvordan et CSS-in-JS-bibliotek du bruker gjengir CSS-en din. For eksempel er folk ofte ikke klar over hvordan stylede komponenter og følelser implementerer dynamiske stiler. Dynamiske stiler er en syntaks som gjør det mulig å bruke JavaScript-funksjoner i stilarterklæringen. Disse funksjonene godtar rekvisitter og returnerer en CSS-blokk.

For å holde kildesorteringsspesifisiteten jevn, genererer begge ovennevnte biblioteker en ny CSS-regel hvis den inneholder en dynamisk deklarasjon og komponenten oppdateres med nye rekvisitter. For å demonstrere hva jeg mener, opprettet jeg denne sandkassen. I JSS bestemte vi oss for å ta en annen avveining, som lar oss oppdatere de dynamiske egenskapene uten å generere nye CSS-regler. *

Bratt læringskurve

For folk som er kjent med CSS, men som er nye i JavaScript, kan det første arbeidet med å komme opp i fart med CSS-in-JS være ganske stort.

Du trenger ikke å være en profesjonell JavaScript-utvikler for å skrive CSS-in-JS, helt til det punktet der kompleks logikk blir involvert. Vi kan ikke generalisere kompleksiteten i styling, da det virkelig avhenger av bruksaken. I tilfeller der CSS-in-JS blir kompleks, er det sannsynlig at implementeringen med vanilje CSS vil være enda mer komplisert.

For grunnleggende CSS-in-JS-styling trenger man å vite hvordan man deklarerer variabler, hvordan man bruker malstrenger og interpolerer JavaScript-verdier. Hvis objektnotasjon brukes, må man vite hvordan man jobber med JavaScript-objekter og den biblioteksspesifikke objektbaserte syntaks. Hvis det dreier seg om dynamisk styling, må man vite hvordan man bruker JavaScript-funksjoner og -balsam.

Totalt sett er det en læringskurve, vi kan ikke benekte den. Denne læringskurven er imidlertid vanligvis ikke mye større enn å lære Sass. Jeg skapte faktisk dette egghead-kurset for å demonstrere dette.

Ingen interoperabilitet

De fleste CSS-in-JS-libs er ikke interoperable. Dette betyr at stiler skrevet med ett bibliotek ikke kan gjengis ved hjelp av et annet bibliotek. Rent praktisk betyr det at du ikke kan bytte hele applikasjonen din enkelt fra en implementering til en annen. Det betyr også at du ikke enkelt kan dele brukergrensesnittet ditt på NPM uten å bringe ditt valgte CSS-in-JS-bibliotek inn i forbrukerens pakke med mindre du har en statisk utvinning for byggetid for CSS.

Vi har begynt å jobbe med ISTF-formatet som skal løse dette problemet, men dessverre har vi ikke hatt tid ennå til å få det til en produksjonsklar tilstand. *

Jeg tror at det å dele gjenbrukbare rammeverk agnostiske UI-komponenter i det offentlige er fremdeles et generelt vanskelig å løse problem.

Sikkerhetsrisiko

Det er mulig å introdusere sikkerhetslekkasjer med CSS-in-JS. Som for alle applikasjoner på klientsiden, må du slippe fra brukerinnspill før du gjengir det, alltid.

Denne artikkelen vil gi deg mer innsikt og noen skrapende eksempler.

Uleselige klassenavn

Noen mennesker synes fortsatt det er viktig at vi holder meningsfylte lesbare klassenavn på nettet. For øyeblikket gir mange CSS-in-JS-biblioteker meningsfulle klassenavn basert på deklarasjonsnavnet eller komponentnavnet i utviklingsmodus. Noen av dem lar deg tilpasse klassens navnegeneratorfunksjon.

I produksjonsmodus genererer de fleste av dem kortere navn for en mindre nyttelast. Dette er en avveining som brukeren av biblioteket må lage og tilpasse biblioteket om nødvendig.

Konklusjon

Tradeoffs eksisterer, og jeg har sannsynligvis ikke engang nevnt dem alle. Men de fleste av dem gjelder ikke universelt for alle CSS-in-JS. De avhenger av hvilket bibliotek du bruker og hvordan du bruker det.

* Det vil ta en dedikert artikkel for å forklare denne setningen. Gi meg beskjed på Twitter (@ oleg008) om hvilken du vil lese mer.