Kako Pretext funkcionira

Kako Pretext funkcioniraKako Pretext funkcionira

14. tra. 2026. - 9 min

Luka Smiljić

Luka Smiljić

Software Engineer


Prošlog tjedna Cheng Lou, bivši član React core tima i autor React Motion-a, objavio je Pretext. Riječ je o TypeScript library-u za mjerenje i postavljanje teksta bez ikakvog kontakta s DOM-om. Veličine od otprilike 15 kilobajta, bez ikakvih drugih dependacy-a i u potpunosti podržava sve Unicode znakove.

Naravno do sada već je dosta ljudi pisalo o Pretextu često uz besmislene tvrdnje poput "CSS je mrtav". No ako se odmaknemo od hype-a, Pretext je stvarno riješio konkretan, dobro poznat performansni problem na čist i temeljit način. Rješenje je dovoljno pametno da ga vrijedi detaljnije pogledati.

Problem koji Pretext rješava

Zašto je mjerenje teksta sporo

Preglednici renderiraju stranice u fazama: izračun stilova, layout, paint, composite. Layout je faza u kojoj preglednik određuje gdje se svaki element nalazi i njegovu veličinu. Kada JavaScript čita layout svojstvo poput offsetHeight, clientWidth ili getBoundingClientRect(), preglednik mora sinhrono dovršiti cijelu layout fazu prije nego što vrati vrijednost. To se zove prisilni sinhroni reflow.

Jedno čitanje košta svega nekoliko mikrosekundi, ali problem je što rijetko trebate samo jedno mjerenje.

Lista s virtualnim scrollanjem treba visinu svakog vidljivog retka. Tablica podataka treba širinu stupaca za svaku ćeliju. Rich text editor treba broj redaka i pozicije miša. Chat sučelje koje prima streaming tokene iznova računa layout za svaki novi komad. U tim situacijama mjerenje se događa stotine puta po frame-u.

Mjerenje 500 blokova teksta putem DOM-a zahtijeva između 15 i 30 milisekundi i uzrokuje 500 zasebnih layout reflow-a. Na Safariju ista operacija traje 149 milisekundi!

Za 60 frames po sekundi, svaki frame ima budžet od 16,67 milisekundi. DOM-bazirano mjerenje teksta za 500 elemenata troši cijeli budžet na Chromeu, a na Safariju ga premašuje 9 puta. Za rendering, animaciju i interakciju ne ostaje ništa.

Na mobilnim uređajima brojke su još gore. Jedan prisilni reflow na prosječnom Android uređaju blokira glavni thread između 10 i 100 milisekundi. Aplikacija izgleda sporo, a developer koji testira na MacBook Prou ne vidi ništa loše.

Libovi za virtualni scroll poput react-window i tanstack-virtual postoje upravo zato što renderiranje 10.000 redaka odjednom uništava performanse. No čak i te libovi trebaju visine redaka. Ako redci sadrže tekst promjenjive duljine, opet se vraćate na mjerenje. A mjerenje znači reflow-e.

Chat aplikacije nailaze na isti zid. AI asistent koji streama tokene u prozor s porukama okida reflow za svaki komad. Pri 20 tokena po sekundi, preglednik izvodi 20 reflow-a u sekundi, povrh svega ostalog što UI radi.

Zašto "grupirajte čitanja" ne rješava problem do kraja

Uobičajeni savjet je grupirati DOM čitanja prije DOM pisanja. To izbjegava isprepletanje čitanja i pisanja, što je dobra praksa. Ali ako mjerite 500 elemenata, preglednik svejedno mora proći cijeli layout pipeline. Na Safariju to traje više od 140ms i efektivno uništava cijeli performansni budžet. Grupiranje pomaže no ne eliminira trošak.

Neki timovi koriste skrivene off-screen elemente. To pomaže vizualnoj stabilnosti, ali ne i performansama. Preglednik svejedno radi layout na skrivenom elementu.

Drugi procjenjuju visinu teksta iz broja znakova ili veličine fonta. To funkcionira za monospace fontove i jednolinjske oznake, ali ne i s proporcionalnim fontovima, višejezičnim sadržajem ili bilo čim što uključuje prelamanje redaka. Greška procjene od 5% na listi od 1.000 stavki znači da je pozicija scrollbara pogrešna za 50 elemenata.

DOM nikad nije bio dizajniran za brzo, ponavljajuće mjerenje teksta. Dizajniran je za renderiranje dokumenata. Svaki zahtjev za mjerenjem prolazi kroz cijeli layout pipeline jer preglednik nema lakši put za davanje dimenzija teksta.

Kako Pretext zapravo funkcionira

Osnovna ideja je jednostavna do te mjere da zvuči pogrešno: prestanite pitati preglednik za dimenzije teksta. Jednom pročitajte metrike fonta putem Canvas API-ja, a zatim sav layout math radite sami.

Dvije funkcije

Funkcija prepare() normalizira whitespace i segmentira tekst koristeći Intl.Segmenter kako bi razumjela Unicode granice. Zatim mjeri svaki segment putem Canvas measureText() API-ja. Svako mjerenje se sprema u cache prema ključu (segment, font).

Ovo se izvršava jednom i rezultati se cachiraju. Budući da Canvas mjerenje ne okida layout reflow, preskače skupi pipeline preglednika u potpunosti. Za 500 paragrafa, prepare() traje oko 17 milisekundi.

Funkcija layout() uzima ta cachirana mjerenja i vrijednost maxWidth, a zatim izračunava prelamanje redaka čistom aritmetikom. Akumulira širine segmenata dok redak ne prelije, prelomi na sljedeći redak i nastavlja. Bez DOM pristupa. Bez reflow-a.

Budući da je ova faza čista matematika, znatno je brža od uobičajenog pristupa. Na Chromeu završava za otprilike 0,09ms, u usporedbi s gotovo 45ms za tradicionalno DOM mjerenje. Na Safariju razlika je 0,12 milisekundi naspram 149 milisekundi.

U apsolutnim brojevima: layout za 500 paragrafa udobno stane unutar jednog frame-a i ostaje 16 milisekundi viška. Možete ga pokrenuti na svakom resize eventu ili svakom pritisku tipke u text editoru. Nikad ne dotakne DOM i nikad ne blokira rendering pipeline.

Unicode dio

Kako Lou sam kaže, "prošao je kroz dubine pakla" da bi prelamanje redaka ispravno radilo za svaki sustav pisanja. Ovo zaslužuje više pažnje jer je upravo to točka gdje većina pristupa mjerenju teksta pada u produkciji.

Jednostavna heuristika broja znakova puca na kineskom, japanskom i korejskom, gdje je svaki znak valjana točka prelamanja. Puca na arapskom i hebrejskom, gdje tekst teče s desna na lijevo i miješa se s lijevim sadržajem u istom paragrafu. Puca na tajlandskom, koji nema razmaka između riječi. I puca na emoji sekvencama, gdje je emoji obitelji tehnički 7 Unicode code pointova renderiranih kao jedan jedini glyph.

Pretext koristi Intl.Segmenter za detekciju grapheme i granica riječi, što znači da prati Unicode specifikaciju za svaki podržani sustav pisanja. CJK dobiva lomove po znaku. Arapski dobiva ispravno bidirektno upravljanje. Emoji sekvence ostaju netaknute.

Lou je navodno postavio rekurzivne petlje testiranja u kojima su AI asistenti pisali logiku prelamanja redaka, testirali je na stvarnom tekstu u stvarnim preglednicima na različitim operativnim sustavima, uspoređivali rezultate i iterirali dok output nije odgovarao piksel po piksel. Tjednima. Ovo je vrsta inženjerskog napora koji opis "knjižnica za mjerenje teksta" ne odaje. Ispravno prelamanje engleskog teksta problem je za vikend. Ispravno prelamanje 20 sustava pisanja u 3 preglednika sasvim je druga kategorija posla.

Caching

Pretext cachira mjerenja fontova prema kombinaciji segmenta i fonta. Ako izmjerite 500 paragrafa u 16px Inter, a zatim još 500 u istom fontu, drugi batch preskače Canvas mjerenje. Cache već ima širine glyphova.

Prozor chata koji prima nove poruke ne mjeri iznova cijelu povijest. Mjeri novu poruku prema postojećem cacheu i dobiva rezultate u mikrosekundama. Responzivni layout koji se preračunava pri resize prozora pokreće layout() s novom vrijednošću maxWidth prema istim cachiranim segmentima. Bez ponovne pripreme.

Library ispravno rukuje i mekim crticama. Ako crtici pripada točka prelamanja, znak crtice pojavljuje se u outputu. Ovo je detalj CSS specifikacije koji većina libova za mjerenje zanemaruje, ali bitno je za uredničke alate i dugački sadržaj gdje prelamanje riječima utječe na broj redaka.

API

Dostupne su tri razine kontrole.

measureNaturalWidth() daje vam širinu teksta kakva bi bila na jednom retku. Korisno za tooltipe, oznake ili bilo koji element gdje trebate znati prirodnu širinu sadržaja.

layoutWithLines(maxWidth) vraća niz objekata redaka. Svaki objekt uključuje početni indeks, završni indeks i širinu retka. Ovo je primarni API za većinu situacija. Dovedete širinu kontejnera i dobijete strukturu redaka.

walkLineRanges(maxWidth, onLine) je niskorazinski callback API. Prolazi kroz točke prelamanja bez alokacije nizova. Koristite ga za obradu po retku u uskoj petlji, poput renderiranja na Canvas ili izračuna kumulativnih visina za virtualni scroll.

Što Pretext omogućuje

Nešto što CSS jednostavno ne podržava: layout teksta vođen proizvoljnom geometrijom, ažuriran svaki frame, bez uništavanja performansi. Ovo otvara prostor za kreativnije, magazinske layoute na webu.

Praktični slučajevi primjene manje su spektakularni, ali šire relevantni. Virtualni scroll s točnim visinama redaka. Tablice podataka gdje se širine stupaca prilagođavaju sadržaju. Editori gdje trebate pozicioniranje kursora bez okidanja reflow-a. Automatsko podešavanje veličine tekstualnih polja. Pozicioniranje tooltipa. Dinamičke oznake na grafikonima. Svaka situacija u kojoj trebate dimenzije teksta prije ili bez renderiranja u DOM. Ako gradite nešto gdje su performanse scrolla, dinamički layout teksta ili precizno pre-render mjerenje važni, Pretext vrijedi dodati u stack.

Pozadina Workspace ureda za kontakt sekciju

Spremni za razgovor?

Pošaljite nam kratak uvod o svom projektu kako bismo dogovorili uvodni poziv. Na pozivu ćemo razgovarati o vašim izazovima i ciljevima te skicirati prve korake prema pravom digitalnom rješenju.

Kako Pretext funkcionira | Workspace