Oliver's Notesblog.mrakovics.com
Vissza

CMS-képek optimalizálása Next.js-ben a getImageProps-szal

Amikor egy CMS-ből, például a WordPress-ből származó tartalmat jelenítesz meg, a képek csupasz <img src="..." alt="..."> tagekként érkezhetnek: nincs srcset, nincs lazy loading, nincs WebP. A Next.js automatikusan nem tud hozzájuk nyúlni, mert egy nyers HTML-stringben élnek, nem pedig egy React component tree-ben. Megnézzük, hogyan használhatod a Next.js getImageProps függvényét arra, hogy megfelelő srcset-et, WebP-konverziót, lazy loadingot és reszponzív méretezést injektálj minden képbe, még mielőtt az eljutna a böngészőbe.

Miért törik meg a CMS-képeken a Next.js optimalizálás

A Next.js képoptimalizálás az egyik legjobb ok arra, hogy a frameworköt használd. Automatikus WebP-konverzió, lazy loading, megfelelő srcset-generálás: mindezt elintézi. De csak akkor működik, ha a next/image-ből származó <Image> komponenst használod.

Amikor egy blogbejegyzés tartalmát húzod le a WordPressből vagy bármilyen headless CMS-ből, egy nyers HTML-stringet kapsz. Az a string sima <img> tageket tartalmaz, amikben semmi más nincs, csak egy src és egy alt. Ezek a képek teljes felbontásban töltődnek be, blokkolják a renderelést, és lehúzzák a Core Web Vitals pontszámodat.

Két lehetőséged van: vagy minden képet React <Image> komponenssé parsolsz renderelési időben, vagy a szerveren előfeldolgozod a HTML-stringet, mielőtt eljutna a klienshez. A szerveroldali megközelítés tisztább: szétválasztja a felelősségeket, és egyszerűen tartja a renderelő komponenseidet.

Mit csinál valójában a getImageProps

A getImageProps egy alacsonyabb szintű API, amelyet a next/image exportál. Ugyanazt az optimalizálási logikát teszi elérhetővé, ami az <Image> komponenst hajtja, csak egy sima aszinkron függvényhívásként, ami JSX helyett propokat ad vissza.

Átadsz neki egy src-t, egy alt-ot, egy sizes-t, és opcionálisan egy width-et és height-et. Visszaad egy props objektumot egy optimalizált src-vel (ami a Next.js kép-CDN-jére mutat), egy teljes srcSet-tel, valamint értelmes alapértelmezésekkel a loading és decoding mezőkhöz. Ezeket a propokat közvetlenül ráteríthetted bármelyik <img> elemre.

Pontosan erre van szükséged. Komponensek renderelése helyett egy stringet dolgozol fel: megkeresed az <img> tageket, lefuttatod a getImageProps-ot, és visszaírod az optimalizált attribútumokat a HTML-stringbe.

Az optimizeImages segédfüggvény: hogyan működik

A segédfüggvény négy apró függvényből épül fel, amelyek mindegyike egyetlen dolgot csinál. Íme, mit csinál az egyes részek, mielőtt beilleszted az implementációt.

A parseImgAttributes fog egy nyers <img> tag stringet, és visszaad egy egyszerű kulcs-érték objektumot az attribútumaiból. Lecsupaszítja a nyitótag szintaxisát, majd egy regexet futtat a maradék szövegen, hogy elkapja az attribútumneveket és -értékeket. A kulcsok kisbetűsek lesznek, így az attrs.src mindig megbízható, függetlenül attól, hogy a CMS hogyan szerializálta a markupot.

A buildImgTag az ellenkezőjét teszi: fogja azt a kulcs-érték map-et, és visszaszerializálja egy valid <img /> stringgé. Escapeli az & és " karaktereket az attribútumértékeken belül, hogy a HTML valid maradjon.

Az optimizeImageTag a lényeg. Meghívja a getImageProps-ot a kiparszolt src-vel és alt-tal, majd visszamergeli a kapott propokat az eredeti attribútum-mapbe. Ha a getImageProps bármilyen okból hibát dob (egy domain, ami nincs a next.config-ban felsorolva, egy hibás URL), a catch blokk lazy loadingot és aszinkron decodingot ad az eredeti taghez, és továbblép.

A CONTENT_IMAGE_SIZES konstans megmondja a böngészőnek, hogy a kép mobilon a teljes viewportot kitölti, szélesebb képernyőkön pedig 768px-nél megáll. Ez egy tipikus blogtartalom-oszlophoz illeszkedik. Igazítsd a saját elrendezésed töréspontjaihoz.

Végül az optimizeImages köti össze az egészet. Egy regexszel megtalálja az összes <img> taget a HTML-stringben, párhuzamosan lefuttatja mindegyiken az optimizeImageTag-et a Promise.all segítségével, majd sorrendben lecseréli az eredeti tageket.

Tovább megyünk: a valódi képméretek lekérése

Talán észrevetted a // width, height kommenteket az eredeti kódban. A getImageProps pontosabb srcset bejegyzéseket generál, ha ismeri a forráskép tényleges méreteit. Ezek nélkül a Next.js egy általános jelöltszélesség-készletre esik vissza, ami egyáltalán nem biztos, hogy illeszkedik a képeidhez.

A méretek megadása a layout shiftet is megelőzi. Amikor a böngésző még a kép betöltése előtt ismeri a képarányt, a megfelelő helyet tudja lefoglalni a dokumentum folyásában. Ez közvetlenül javítja a Cumulative Layout Shift (CLS) pontszámodat.

A probe-image-size npm package épp annyi bájtot olvas be egy kép URL-jéből, amennyi a méretek kinyeréséhez kell: nem tölti le a teljes fájlt. Előbb telepítsd:

npm install probe-image-size
npm install -save-dev @types/probe-image-size

Ezután add hozzá ezt a függvényt a segédfájlodhoz:

A HTML biztonságos renderelése a html-react-parser-rel

Miután az optimizeImages feldolgozta a HTML-stringedet, renderelned kell azt Reactben. Az első lehetőség, amihez a legtöbb fejlesztő nyúl, a dangerouslySetInnerHTML. A neve nem csak egy figyelmeztetés: pontosan leírja, mit csinál.

Az innerHTML közvetlen beállítása teljesen megkerüli a Reactet. A React nem tudja reconcile-olni azokat a DOM node-okat, nem tud event handlereket csatolni a bennük lévő elemekre, és nem tudja megakadályozni a cross-site scriptinget (XSS), ha a CMS-tartalom valaha is felhasználó által generált vagy kompromittált. A hidratáció helyességét is elveszíted: a szerver és a kliens eltérő DOM-okat állíthat elő, ami alattomos hibákat okoz.

A megfelelő eszköz a html-react-parser. Ez a HTML-stringet egy React elemfává parsolja, nem nyers DOM node-okká. A React birtokolja a kimenetet, a hidratáció helyesen működik, és a replace opcióval bármelyik elemet elkaphatod, hogy szükség esetén egy egyedi komponensre cseréld.

MegközelítésXSS-biztosReact reconciliationEvent handlerekHidratáció
dangerouslySetInnerHTMLNemNemNemMegbízhatatlan
html-react-parserIgenIgenIgenHelyes

A replace callback biztonsági hálóként is hasznos. Ha bármelyik <img> tag átcsúszott a szerveroldali optimalizálási lépésen, itt elkaphatod, és helyette egy Next.js <Image> komponenst rendelhetsz. Tiszta belépési pontot ad a parse-fába anélkül, hogy saját HTML-bejárót kellene írnod.

Tegyük össze az egészet

A teljes pipeline egyszerű. Lekéred a bejegyzés törzsét a CMS-ből, a szerveren átengeded a HTML-stringet az optimizeImages-en, majd az eredményt átadod a komponensednek, és a html-react-parser-rel rendereled. Az olvasóid megfelelően méretezett, lazy loadolt képeket kapnak WebP formátumban, te pedig egyetlen sablonhoz vagy a CMS-hez sem nyúltál hozzá.

A megközelítés jól skálázódik is. Mivel az optimizeImages build időben vagy egy Server Componentben fut, nincs kliensoldali költség. A nehéz munka mind azelőtt történik, hogy a HTML elérné a böngészőt.