Derfor bør du prøve splitter nye «flex-wrap: balance»

Se hvordan du bruker den nye CSS-verdien, hvordan verifisering av epost-adresser snart kan gjøres i nettleseren og hvordan Zod 4.5 bruker mye mindre minne, i ukas ForrigeUke.

Publisert

< ForrigeUke />

ForrigeUke er en artikkelserie som oppsummerer hva som skjedde i frontend-verden i uka som var. Innleggene skrives av frontend-faggruppene til Bekk, og kan også følges på bekk.no/fag.

Dette var uken for tegneserier ✍️, konstruktive tilbakemeldinger 😇, bestevenner 👯♀️

— og 672 ting som skjedde i frontend-verdenen

Balanserte uttrykk med flex-wrap: balance ⚖️

Det er aldri trivielt å tilpasse nettsider til forskjellige skjermstørrelser.

flex-wrap er en nyttig egenskap som lar elementer flyte over på flere linjer når forelder-containeren blir for liten. Men er skjermen stor nok, kan ett enkelt element havne på en helt egen linje og skape veldig mye tomrom.

På bildet nedenfor får for eksempel Instagram-logoen litt for mye luft:

SoMe-kanaler med Instagram-logoen alene på egen linje. Hentet fra bloggen til Ahmad Shadeed

Dette kan løses på mange måter. Én mulighet er å trikse litt med display: contents:

<div class="channels">
  <div class="group-1">
    <!-- Første gruppe med SoMe-kanaler -->
  </div>
  <div class="group-2">
    <!-- Andre gruppe med SoMe-kanaler -->
  </div>
</div>
.channels {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 1rem;

  > div {
    /* Fjern gruppene på små skjermer */
    display: contents;

    @container (width >= 500px) {
      display: inherit;
      flex-wrap: inherit;
      justify-content: inherit;
      gap: inherit;
    }
  }
}

Over 500px har gruppe-div-ene display: flex, og hele gruppen havner samlet på en ny linje. Når containerbredden blir mindre enn 500px, slår display: contents inn: gruppene forsvinner ut av layouten, flex-wrap: wrap virker direkte på SoMe-kanalene, og de kan wrappe fritt.

Da kan man riktignok igjen ende opp med ett element alene på en linje, men på det tidspunktet er containeren liten nok til at det ikke bør se like rart ut.

Resultatet med display: contents. Hentet fra bloggen til Ahmad Shadeed

Legg likevel merke til hvor mye CSS som skal til for en halvgod løsning. Halvgod nettopp fordi den fortsatt kan se rar ut på mindre skjermer, og fordi den ryker så snart vi legger til eller fjerner elementer.

Forrige uke skrev Ahmad Shadeed en artikkel om en splitter ny CSS-verdi som nylig kom ut i Google Chrome 150: flex-wrap: balance. Verdien balanserer elementene automatisk, slik at du slipper å definere brekkpunkter manuelt.

.channels {
  display: flex;
  flex-wrap: balance;
  justify-content: center;
  gap: 1rem;
}
Resultatet med flex-wrap: balance. Hentet fra bloggen til Ahmad Shadeed

Vi får en finere og mer balansert fordeling med mye mindre kode!

Per nå er balance-verdien kun tilgjengelig i Chromium-baserte nettlesere, men WebKit har allerede flettet inn en tilsvarende PR, og Mozilla er i gang med implementeringen, så vi kan regne med at den snart kommer i både Safari og Firefox.

Verifisering av epost-adresser i nettleser 📨

Det er vanlig å bekrefte e-postadresser ved registrering. Med en enkel verifisering, der brukeren må skrive inn en kode som er sendt til den oppgitte adressen, unngår man at noen registrerer mange brukere med tilfeldige e-postadresser. Ulempen med denne flyten er at brukeren må forlate nettsiden din for å finne koden.

Forrige uke skrev Phil Nash om et forslag som delegerer hele bekreftelsen til nettleseren, slik at brukeren slipper å åpne e-post-klienten sin.

Det foreslåtte e-post-APIet fungerer ved at brukeren først logger inn på e-postklienten sin i nettleseren, og klienten registrerer innloggingen der. Når brukeren senere skriver e-postadressen sin i et input-felt, sjekker nettleseren om brukeren har vært innlogget med den samme adressen. I så fall genererer nettleseren og e-postleverandøren et token sammen. Tokenet sendes til serveren sammen med e-postadressen, og kan valideres med en npm-pakke:

import { verifyEmailToken } from "email-verification-api";

const result = await verifyEmailToken({ email, token, audience, nonce });

Valideringen sjekker blant annet at e-postadressen er ekte og tilhører brukeren, ved å bekrefte at tokenet er riktig utformet, ikke utløpt, og utstedt og signert av en gyldig aktør.

APIet er i en tidlig forslagsfase, og forfatteren innrømmer at det er mye arbeid som gjenstår. Foreløpig er det kun støttet i Google Chrome som en "origin trial" (eksperimentell funksjonalitet), med Gmail som e-post-klient. Det virker uansett som et veldig godt forslag, og det skal bli spennende å følge med på om det blir videreutviklet.

Zod 4.5 spiser mindre minne 😋

Versjon 4.5 av Zod kom ut forrige uke, og spiser nå opptil 9.8x mindre minne enn 4.4. Hvordan er det mulig? La oss først se litt nærmere på hvordan JavaScript bygger opp objekter.

Hvert objekt har attributter, og et attributt som er en funksjon, kalles en metode. Se for deg at du har flere hundre objekter som representerer personer. For eksempel:

{
  navn: "Joachim",
  yrke: "Utvikler"
}

La oss si at vi ønsker å gi alle objektene en foo()-metode. Én måte er å la hvert objekt definere sin egen funksjon, men da lagrer vi hundrevis av identiske kopier. Dette er ugunstig når alle funksjonene vil oppføre seg likt.

Derfor introduserte JavaScript "prototyper". En prototype er en samling av felles funksjonalitet. Kaller du foo() på et objekt som ikke selv har metoden, sjekker JavaScript om objektet peker på en prototype, og kaller definisjonen der. Slik kan vi ha flere hundre objekter, men bare én funksjonsdefinisjon.

Hvis objektet har definert sin egen foo(), blir prototypen aldri sjekket. Vi sier at objektet "skygger" den delte definisjonen.

Det høres jo ut som et veldig fint system. Så hva er egentlig ulempen?

Den dukker opp når du river en metode løs fra objektet sitt:

const { parse } = z.string();

En metode må vite hvilket objekt den jobber på, og normalt får den det fra kallstedet. Skriver du skjema.parse("hei"), setter JavaScript this til skjema. Men når parse ligger i en egen variabel, er det ingen som forteller den hvilket skjema den skal validere mot, og kallet feiler.

En løsning er å binde parse-metoden til skjemaet den kom fra i konstruktøren:

class ZodString {
  constructor(def) {
    this.parse = ZodString.prototype.parse.bind(this);
    // ...gjør det samme for de andre 48 metodene
  }
}

Her blir det laget en kopi av ZodString.prototype.parse, med ZodString-instansen som this. Kallstedet får ikke lenger bestemme hva this skal være, for det er avgjort på forhånd. Derfor kan parse løsrives og fortsatt kalles uten problem. Zod 4.4 bandt alle de 49 metodene i prototypen og la dem på hver enkelt instans, der de skygget originalene. På grunn av denne skyggingen, fantes prototypen, men den ble aldri brukt.

Å binde 49 metoder bruker mye minne fordi V8, JavaScript-motoren i Node og Chromium-baserte nettlesere, har en sær måte å lagre attributter på. Hvis et objekt har mindre enn 13 attributter, holdes de i et kompakt lager på 128 bytes. Med 13 eller flere, hopper lageret til 848 bytes, og med 21 eller flere trengs 1616 bytes. Med 49 metoder landet hvert Zod-objekt alltid i den største bøtten, selv om man typisk bare bruker et fåtall av de tilgjengelige metodene.

Grepet i 4.5 er å utsette bindingen. Istedenfor å holde på 49 ferdig-bundede metoder, inneholder prototypen nå gettere som lar objektet hente en bundet kopi først når den trengs. Første gang du er innom .optional, går oppslaget til getteren i prototypen, og kopien legges på objektet som en skyggekopi. Neste gang ligger skyggekopien der, og prototypen blir aldri nådd. Men istedenfor å kopiere alle de 49 metodene med en gang, kopieres bare de du faktisk bruker. Og siden getteren returnerer en bundet funksjon, virker const { parse } = z.string() fortsatt like godt.

Slik klarte Zod å redusere minneforbruket til z.string() fra 7.5kB til bare 784 bytes.

Men det er jo snakk om så lite minne. Har det egentlig noe å si?

Zod er en avhengighet i over 120 tusen pakker i npm-registeret. Det er altså nesten garantert én eller flere instanser av Zod i frontend-prosjektet ditt, og et skjema består sjeldent av ett felt. Artikkelen skriver at et z.object() med ti nøkler gikk fra 82kB til 11kB. Hvis du har hundrevis av skjemaer i minnet, kan forskjellen være på flere megabyte.

En stooor applaus til Zod-teamet! 👏

Det var alt for denne gang, ha en fin uke 👋

Foretrekk oss i Google Discover

Ved å legge oss til som foretrukket kilde i Google vil du blant annet få opp flere av sakene våre i Google Discover. Tusen takk for støtten!

Foretrekk oss 😻
Bygget med Labrador CMS