content-visibility: renderointi näytön ulkopuolella ilman listan virtualisointia
content-visibility: renderointi näytön ulkopuolella ilman listan virtualisointia
Artikkelin alla oleva tuhannen kommentin lista renderöityy huomattavan hitaasti, ja sen vieritys nykii heikommalla puhelimella. Reactin vakiovastaus tähän on virtualisointi – react-window tai @tanstack/react-virtual – joka renderöi DOM:iin vain ne elementit, jotka mahtuvat kulloinkin näkyvään ikkunaan, plus pienen puskurin. Se toimii, mutta sillä on kustannus, josta harvoin puhutaan käyttöönoton yhteydessä: ikkunan ulkopuoliset elementit eivät ole piilotettuja, ne eivät ole olemassa DOM:ssa. Ctrl+F:n painaminen ei löydä tekstiä kommentista #850, ennen kuin vierität sinne käsin. Sivun tulostus näyttää vain sen, mikä oli renderöitynä tulostuskomennon hetkellä. Ruudunlukijan navigointi otsikoiden tai maamerkkien kautta ohittaa sisällön, jota ei fyysisesti ole puussa – tämä ei ole virtualisoinnin bugi, vaan sen väistämätön seuraus.
content-visibility hyökkää samaan suorituskykyongelmaan täysin eri suunnasta: elementit pysyvät DOM:ssa, aina, kokonaisuudessaan. Selain vain jättää niiden kohdalla väliin työn, joka sen normaalisti pitäisi tehdä – layoutin, piirtämisen, kompositointikerrosten luomisen – kunnes elementti on riittävän lähellä näkyvää aluetta, jotta tuo työ on ylipäätään järkevää.
Mitä "väliin jättäminen" tarkalleen tarkoittaa
content-visibility: auto elementillä pakottaa selaimen soveltamaan siihen CSS Containmentia – täsmälleen samaa mekanismia, joka Container Queries -artikkelissa mahdollisti silmukoitumisen välttämisen säiliön kokoa kyseltäessä. Tässä containment palvelee eri tarkoitusta: jos elementti on näytön ulkopuolella, selain voi turvallisesti olettaa, että sen sisäinen layout ja piirtäminen eivät vaikuta mihinkään sen ulkopuolella, joten se voi lykätä ne kokonaan, laskematta yhtäkään pikseliä sisällä.
css
.comment {content-visibility: auto;contain-intrinsic-size: auto 180px;}
contain-intrinsic-size ei ole kosmeettinen lisä – ilman sitä ohitettu elementti kutistuisi nollakorkuiseksi, koska selaimella ei ole mistä ottaa sen kokoa, kun se ei laske layoutia sisällä. Tämä on täsmälleen sama ongelma kuin container-type: size ilman eksplisiittistä korkeutta – paitsi että tässä seurauksena ei ole katoava kortti, vaan hyppivä vierityspalkki, koska dokumentin korkeus muuttuu yhtäkkiä joka kerta, kun seuraava kommentti siirtyy ohitustilaan tai pois siitä. Arvo auto 180px kertoo selaimelle kaksi asiaa kerralla: käytä 180px:ää ensimmäisenä arviona, ennen kuin elementti on kertaakaan oikeasti renderöity, ja sen jälkeen muista sen todellinen, laskettu koko ja käytä tuota muistettua arvoa paikkamerkkinä joka seuraavalla ohituskerralla – joten jos todelliset kommentit ovat eri korkuisia, selain arvaa ajan mittaan yhä tarkemmin, kuinka paljon tilaa varata, sen sijaan että pitäytyisi jäykästi yhdessä luvussa.
Temppu, jota et saa täydellä virtualisoinnilla: hidden until found
Tässä kohtaa content-visibility tekee jotain, mihin virtualisointi ei periaatteessa kykene – ei vain käytännössä. auto-tilassa oleva elementti, vaikka renderöinnin osalta ohitettu, pitää tekstinsä yhä fyysisesti DOM:ssa. Selain tietää tämän ja kohtelee tällaista elementtiä "piilotettuna mutta löydettävänä" (hidden but matchable): kun käyttäjä painaa Ctrl+F:ää ja kirjoittaa fraasin, joka täsmää ohitetun elementin sisällä olevaan tekstiin, selain peruu ohituksen tilapäisesti, renderöi elementin oikeasti, korostaa osuman ja vierittää sen kohdalle – automaattisesti, ilman yhtään riviä koodia sinun puoleltasi. display: none ei koskaan tehnyt tätä (Ctrl+F yksinkertaisesti ohittaa sen), eikä virtualisoitu lista voi tehdä tätä, koska etsimääsi tekstiä ei yksinkertaisesti ole DOM:ssa, ennen kuin vierität sinne käsin.
Käytännön esimerkki: kommenttilista ilman ainuttakaan riviä virtualisointi-JS:ää
jsx
const CommentList = ({ comments }) => (<ul className="comment-list">{comments.map((comment) => (<li key={comment.id} className="comment"><p className="comment__author">{comment.author}</p><p className="comment__body">{comment.body}</p></li>))}</ul>);export default CommentList;
css
.comment {content-visibility: auto;contain-intrinsic-size: auto 180px;padding: 12px 0;border-bottom: 1px solid #e5e5e5;}
Koko komponentti renderöi kaikki tuhat <li>-elementtiä DOM:iin – ei mitään vieritysikkunan logiikkaa, ei laskentaa siitä, mitkä elementit ovat kulloinkin näkyvissä, ei kirjastoa. Selain päättää itsenäisesti, minkä tuhannesta kommentista layout ja piirto kannattaa laskea kulloinkin, ja mitkä "eivät ole olemassa" puhtaasti renderöinnin kannalta, vaikka ne ovat DOM:ssa kokonaisuudessaan.
Milloin tämä ei riitä – ja miksi virtualisoinnilla on yhä paikkansa
content-visibility ratkaisee renderöinnin kustannuksen – layoutin ja piirron. Se ei ratkaise kustannusta, joka React-sovelluksessa usein sattuu enemmän: jokainen tuhannesta <li>-elementistä on yhä oikea React-komponentti, joka mountataan, hydratoidaan ja jolla voi olla omat hookinsa ja efektinsä. CSS-renderöinnin ohittaminen ei saa näitä tuhatta komponentti-instanssia lakkaamaan olemasta React-puussa, eikä se poista niiden luomiseen tarvittavan JavaScriptin kustannusta. Yksinkertaisille, enimmäkseen staattisille sisältölohkoille (kommentit, pitkän artikkelin kappaleet, ei-interaktiiviset taulukon rivit) tällä ei ole merkitystä – todellinen kustannus oli joka tapauksessa layoutissa ja piirrossa, ei komponentin logiikassa. Listoille, jotka koostuvat raskaista, interaktiivisista komponenteista – kullakin oma tilansa, tilaukset, kallis renderöinti – todellinen virtualisointi, joka poistaa käyttämättömät instanssit React-puusta kokonaan, on yhä joskus ainoa tapa säilyttää sulavuus.
Sudenkuopat ja hyvät käytännöt
contain-intrinsic-sizeon käytännössä pakollinen, ei valinnainen. Sen jättäminen pois johtaa hyppivään vieritykseen ja väärään dokumentin korkeuteen joka kerta, kun elementti siirtyy ohitustilaan ja takaisin.content-visibility: hiddenei ole sama asia kuinauto.hiddenohittaa renderöinnin aina, riippumatta sijainnista näytöllä, mutta toisin kuindisplay: none, se säilyttää lasketun layoutin sisäisen tilan välimuistissa – elementin näyttäminen uudelleen (esim. välilehden vaihto) on halvempaa kuindisplay: none:n jälkeen, koska selaimen ei tarvitse laskea kaikkea alusta.content-visibility: hidden-elementti poistetaan tällöin oikein myös saavutettavuuspuusta, aivan kutendisplay: none– tämä on turvallinen valinta usein vaihdettaville välilehdille ja paneeleille, ei pelkkä suorituskykytemppu.- Tämä ei korvaa virtualisointia listoissa, joissa on raskaita komponentteja. Kohtele sitä ensimmäisenä, halvempana työkaluna yksinkertaisille, enimmäkseen staattisille listoille – tartu oikeaan virtualisointiin vasta, kun profiloija todella osoittaa kustannuksen olevan JS:n/Reactin puolella, ei pelkässä layoutissa.
- Tarkista tuki ennen kuin luotat täysin ominaisuuteen "hidden but matchable". Pelkällä
content-visibility: auto-ominaisuudella on nykyään laaja tuki kaikissa suurissa moottoreissa, mutta "tilapäisen paljastamisen" käytös Ctrl+F:n yhteydessä on usein hiotuinta Chromium-pohjaisissa selaimissa – kohtele sitä mukavana lisänä, älä takeena, jonka varaan rakennat saavutettavuusvaatimuksen.
Yhteenveto
Virtualisointi ja content-visibility ratkaisevat ensi silmäyksellä saman ongelman – pitkän listan, joka renderöityy liian hitaasti – mutta ne tekevät sen täysin eri tasoilla. Virtualisointi poistaa elementit DOM:sta ja React-puusta kokonaan, ja maksaa siitä Ctrl+F:n, tulostuksen ja osan saavutettavuusnavigoinnin menetyksellä. content-visibility jättää DOM:n kokonaiseksi ja käskee selainta ohittamaan vain kalliin renderöintityön sille, mikä on kulloinkin näkymättömissä – vastineeksi saat vähemmän aggressiivisen optimoinnin, mutta ilmaiseksi, rikkomatta mitään, mitä selain jo osasi tehdä täydelle dokumentille. Useimmille pitkille, kohtuullisen yksinkertaisille listoille tämä on oikea ensimmäinen työkalu – virtualisointi jää varalle tapauksiin, joissa tämä ei todella riitä.