Udvekslingen af ​​CSS-in-JS

Foto af Artem Bali

For nylig skrev jeg et overblik over CSS-in-JS på højere niveau, mest for at tale om de problemer, denne tilgang prøver at løse. Biblioteksforfattere investerer sjældent tid i at beskrive fordelingen af ​​deres løsning. Nogle gange skyldes det, at de er for partiske, og nogle gange ved de ikke, hvordan brugerne anvender værktøjet. Så dette er et forsøg på at beskrive de kompromiser, jeg har set hidtil. Jeg synes, det er vigtigt at nævne, at jeg er forfatteren af ​​JSS, så jeg bør betragtes som partisk.

Social påvirkning

Der er et lag mennesker, der arbejder på webplatformen og ikke kender noget JavaScript. Disse mennesker får betalt for at skrive HTML og CSS. CSS-in-JS har haft en enorm indflydelse på udviklernes arbejdsgang. En virkelig transformativ forandring kan aldrig ske, uden at nogle mennesker bliver efterladt. Jeg ved ikke, om CSS-in-JS skal være den eneste måde, men masseoptagelsen er et klart tegn på problemer med at bruge CSS i moderne applikationer.

En stor del af problemet er vores manglende evne til at kommunikere nøjagtigt brugenstilfælde, hvor CSS-in-JS lyser, og hvordan vi bruger det korrekt til en opgave. Mange CSS-in-JS-entusiaster har haft succes med at promovere teknologien, men ikke mange kritikere talte om kompromisserne på en konstruktiv måde uden at tage billige gynger på værktøjerne. Som et resultat forlod vi mange afvejninger skjulte og gjorde ikke en stærk indsats for at give forklaringen og løsningen.

CSS-in-JS er et forsøg på at gøre sager med kompleks brug lettere at håndtere, så skub ikke den, hvor det ikke er nødvendigt!

Kørselsomkostninger

Når CSS genereres fra JavaScript ved kørsel i browseren, er der en iboende overhead. Kørselsomkostninger varierer fra bibliotek til bibliotek. Dette er en god generisk benchmark, men sørg for at lave dine egne test. Store forskelle ved runtime vises afhængigt af behovet for at have en fuld CSS-parsing af skabelonstrenge, mængde optimeringer, dynamiske stils implementeringsdetaljer, hash-algoritme og omkostningsrammer til integrering. *

Udover den potentielle løbetid overhead, skal du overveje 4 forskellige sammenlægningsstrategier, fordi nogle CSS-in-JS-biblioteker understøtter flere strategier, og det er op til brugeren at anvende dem. *

Strategi 1: Kun kørselsgenerering

Runtime CSS-generation er en teknik, der genererer en CSS-streng i JavaScript og derefter indsprøjter denne streng ved hjælp af et typografi i dokumentet. Denne teknik producerer et typografiark, IKKE inline stilarter.

Udvekslingen af ​​runtime-generation er manglende evne til at levere stylet indhold på et tidligt tidspunkt, når dokumentet begynder at indlæses. Denne tilgang passer normalt til applikationer uden indhold, der kan være nyttigt med det samme. Normalt kræver sådanne applikationer brugerinteraktioner, før de virkelig kan blive nyttige for en bruger. Ofte fungerer sådanne applikationer med indhold, der er så dynamisk, at det bliver forældet, så snart du indlæser det, så du er nødt til at etablere en opdateringspipeline tidligt på for eksempel Twitter. Derudover, når en bruger er logget ind, er der ikke behov for at angive HTML til SEO.

Hvis interaktionen kræver JavaScript, skal pakken indlæses, før appen er klar. For eksempel kan du vise indholdet af en standardkanal, når du indlæser Slack i dokumentet, men det er sandsynligt, at brugeren vil ændre kanalen lige efter det. Så hvis du indlæste det oprindelige indhold bare for at kaste dem væk med det samme.

Oplevet ydelse af sådanne applikationer kan forbedres med pladsholdere og andre tricks for at lade applikationen føles mere øjeblikkelig end den faktisk er. Sådanne applikationer er normalt data tunge alligevel, så de vil ikke være nyttige så hurtigt som en artikel.

Strategi 2: Kørselsgenerering med kritisk CSS

Kritisk CSS er den minimale mængde CSS, der kræves for at style siden i dens oprindelige tilstand. Det gengives ved hjælp af et typografi i hovedet på dokumentet. Denne teknik er vidt brugt med og uden CSS-in-JS. I begge tilfælde vil du sandsynligvis dobbelt indlæse CSS-reglerne, en gang som en del af det kritiske CSS og en gang som en del af JavaScript- eller CSS-bundtet. Størrelsen på Critical CSS kan være ganske stor afhængigt af mængden af ​​indholdet. Normalt vil cache ikke blive cache.

Uden kritisk CSS, skal en statisk indholdstung enkel side-applikation med runtime CSS-in-JS vise pladsholdere i stedet for indhold. Dette er dårligt, fordi det kunne have været nyttigt for en bruger meget tidligere, hvilket forbedrede tilgængeligheden på enheder med lav ende og til forbindelser med lav båndbredde.

Med kritisk CSS kan runtime CSS-generation udføres på et senere tidspunkt uden at blokere UI'en i den indledende fase. Vær dog advaret om, at low-end mobile enheder, som er cirka 5 år gamle, kan CSS-generation fra JavaScript have en negativ indflydelse på ydelsen. Det afhænger stærkt af mængden af ​​CSS, der genereres, og det anvendte bibliotek, så det kan ikke generaliseres.

Udvekslingen af ​​denne strategi er omkostningerne ved kritisk CSS-ekstraktion og omkostningerne til runtime CSS-generation.

Strategi 3: Kun bygningstidsekstraktion

Denne strategi er den standardstrategi på nettet uden CSS-in-JS. Nogle CSS-in-JS-biblioteker giver dig mulighed for at udtrække statisk CSS på bygningstidspunktet. * I dette tilfælde er der ikke tale om noget runtime-overhead, CSS gengives på siden ved hjælp af et linktag. Omkostningerne ved CSS-generationen betales en gang i forvejen.

Der er 2 store afvejninger her:

  1. Du kan ikke bruge nogle af de dynamiske API'er CSS-in-JS-tilbud på runtime, fordi du ikke har adgang til staten. Ofte kan du stadig ikke bruge CSS-brugerdefinerede egenskaber, fordi de ikke understøttes i enhver browser og ikke kan polyfyldes på bygningstidspunktet af natur. I dette tilfælde bliver du nødt til at foretage løsninger til dynamisk tema og statsbaseret styling. *
  2. Uden kritisk CSS og med en tom cache blokerer du den første maling, indtil dit CSS-bundt bliver indlæst. Et linkelement i hovedet af dokumentet blokerer gengivelsen af ​​HTML.
  3. Ikke-deterministisk specificitet med sidebaseret bundtsopdeling i applikationer på en side. *

Strategi 4: Bygningstidsekstraktion med Critical CSS

Denne strategi er heller ikke unik for CSS-in-JS. Fuld statisk ekstraktion med kritisk CSS leverer den bedste ydelse, når du arbejder med en mere statisk applikation. Denne tilgang har stadig de førnævnte afvejninger af en statisk CSS, bortset fra at tagget til blokeringslink kan flyttes til bunden af ​​dokumentet.

Der er fire vigtigste CSS-renderingstrategier. Kun 2 af dem er specifikke for CSS-in-JS, og ingen af ​​dem gælder for alle biblioteker.

Tilgængelighed

CSS-in-JS kan mindske tilgængeligheden, når den bruges på den forkerte måde. Dette vil ske, når et stort set statisk indholdsside implementeres uden kritisk CSS-ekstraktion, så HTML ikke kan males, før JavaScript-bundtet indlæses og evalueres. Dette kan også ske, når en enorm CSS-fil gengives ved hjælp af et blokerende link-tag i hovedet af dokumentet, som er det mest populære aktuelle problem med den traditionelle indlejring og ikke specifikt for CSS-in-JS.

Udviklere skal tage ansvar for tilgængelighed. Der er stadig en stærk misforstået idé om, at en ustabil internetforbindelse er et problem for økonomisk svage lande. Vi har en tendens til at glemme, at vi har forbindelsesproblemer hver eneste dag, når vi går ind i et underjordisk jernbanesystem eller en stor bygning. En stabil kabelfri mobilforbindelse er en myte. Det er ikke engang nemt at have en stabil WiFi-forbindelse, f.eks. Et 2,4 GHz WI-FI-netværk kan få interferens fra en mikrobølgeovn!

Prisen for kritisk CSS med server-side-rendering

For at få kritisk CSS-ekstraktion til CSS-in-JS, har vi brug for SSR. SSR er en proces til generering af den endelige HTML for en given tilstand af en applikation på serveren. Faktisk kan det være en ganske kompliceret og dyr proces. Det kræver en vis mængde CPU-cyklusser på serveren for hver HTTP-anmodning.

CSS-in-JS udnytter normalt det faktum, at det er koblet til HTML-rendering pipeline. * Det ved, hvad HTML blev gengivet, og hvilket CSS det har brug for, så det er i stand til at producere den absolutte minimale mængde af den. Kritisk CSS tilføjer ekstra overhead til HTML-gengivelse på serveren, fordi denne CSS også skal kompileres til en endelig CSS-streng. I nogle scenarier er det imidlertid svært eller endda umuligt at cache på serveren.

Rendering sort kasse

Du skal være opmærksom på, hvordan et CSS-in-JS-bibliotek, du bruger, gengiver din CSS. For eksempel er folk ofte ikke opmærksomme på, hvordan stylede komponenter og følelser implementerer dynamiske stilarter. Dynamiske stilarter er en syntaks, der tillader brug af JavaScript-funktioner inde i din typedeklaration. Disse funktioner accepterer rekvisitter og returnerer en CSS-blok.

For at holde kildesordens specificitet konsistent genererer begge ovennævnte biblioteker en ny CSS-regel, hvis den indeholder en dynamisk deklaration og komponenten opdateres med nye rekvisitter. For at demonstrere, hvad jeg mener, oprettede jeg denne sandkasse. I JSS besluttede vi at tage en anden afvejning, som giver os mulighed for at opdatere de dynamiske egenskaber uden at generere nye CSS-regler. *

Stejl indlæringskurve

For folk, der er bekendt med CSS, men som er nye med JavaScript, kan den indledende mængde arbejde for at komme op med hastigheden med CSS-in-JS muligvis være ganske stor.

Du behøver ikke at være en professionel JavaScript-udvikler for at skrive CSS-in-JS, indtil det punkt, hvor kompleks logik bliver involveret. Vi kan ikke generalisere kompleksiteten af ​​styling, da det virkelig afhænger af brugssagen. I tilfælde, hvor CSS-in-JS bliver kompleks, er det sandsynligt, at implementeringen med vanilla CSS ville være endnu mere kompliceret.

Til grundlæggende CSS-in-JS-styling skal man vide, hvordan man deklarerer variabler, hvordan man bruger skabelonstrenge og interpolerer JavaScript-værdier. Hvis der anvendes objektnotation, skal man vide, hvordan man arbejder med JavaScript-objekter og den biblioteksspecifikke objektbaserede syntaks. Hvis der er tale om dynamisk styling, skal man vide, hvordan man bruger JavaScript-funktioner og -betingelser.

Generelt er der en læringskurve, vi kan ikke benægte den. Denne læringskurve er dog normalt ikke meget større end at lære Sass. Faktisk skabte jeg dette egghead-kursus for at demonstrere dette.

Ingen interoperabilitet

De fleste CSS-in-JS-libs er ikke interoperable. Dette betyder, at stilarter, der er skrevet med et bibliotek, ikke kan gengives ved hjælp af et andet bibliotek. Praktisk set betyder det, at du ikke let kan skifte hele din applikation fra en implementering til en anden. Det betyder også, at du ikke nemt kan dele dit brugergrænseflade på NPM uden at bringe dit valgte CSS-in-JS-bibliotek ind i forbrugerens bundt, medmindre du har en statisk ekstraktion til bygningstid til din CSS.

Vi er begyndt at arbejde på det ISTF-format, der formodes at løse dette problem, men desværre har vi ikke haft tid endnu til at få det til en produktionsklar tilstand. *

Jeg synes, at deling af genanvendelige rammer agnostiske UI-komponenter i det offentlige rum stadig er et generelt vanskeligt at løse problem.

Sikkerhedsrisici

Det er muligt at introducere sikkerhedslækager med CSS-in-JS. Ligesom med alle applikationer på klientsiden, skal du undslippe brugerinput, før du gengiver det, altid.

Denne artikel giver dig mere indsigt og nogle forskrækkende eksempler.

Ulæselige klassenavne

Nogle mennesker synes stadig, at det er vigtigt, at vi holder meningsfulde læsbare klassenavne på nettet. I øjeblikket leverer mange CSS-in-JS-biblioteker meningsfulde klassenavne baseret på deklarationsnavnet eller komponentnavnet i udviklingsfunktion. Nogle af dem giver dig endda mulighed for at tilpasse funktionen til klassens navnegenerator.

I produktionstilstand genererer dog de fleste af dem kortere navne til en mindre nyttelast. Dette er en afvejning, som brugeren af ​​biblioteket skal fremstille og tilpasse biblioteket om nødvendigt.

Konklusion

Der findes tradeoffs, og jeg nævnte sandsynligvis ikke engang dem alle. Men de fleste af dem gælder ikke universelt for alle CSS-in-JS. De afhænger af hvilket bibliotek du bruger, og hvordan du bruger det.

* Det kræver en dedikeret artikel at forklare denne sætning. Fortæl mig på Twitter (@ oleg008) om, hvilken du gerne vil læse mere.