Hogyan lehet azonosítani és megoldani az elpazarolt rendereléseket a React

Szóval nemrégiben egy reak-alkalmazás teljesítményprofilozására gondoltam, amin dolgoztam, és hirtelen arra gondoltam, hogy beállítok néhány teljesítménymutatót. És rájöttem, hogy az első dolog, amivel foglalkoznom kell, az a pazarolt renderelés , amelyet az egyes weboldalakon végzek. Lehet, hogy azon gondolkodik, hogy egyébként mit pazarolnak el? Merüljünk le.

A React kezdettől fogva megváltoztatta a webalkalmazások építésének filozófiáját és azt követően a front-end fejlesztők gondolkodásmódját. A Virtual DOM bevezetésével a React a kezelőfelület frissítéseit a lehető leghatékonyabbá teszi. Ez rendezetté teszi a webalkalmazás élményét. Gondolkodott már azon, hogyan lehetne gyorsabbá tenni a React alkalmazásokat? Miért hajlamosak a közepes méretű React webalkalmazások még mindig gyengén teljesíteni? A problémák abban rejlenek, hogy valójában hogyan használjuk a React!

Hogyan működik a React

Egy olyan modern előtér-könyvtár, mint a React, nem teszi csodálatosabbá az alkalmazásunkat. Először is nekünk, fejlesztőknek meg kell értenünk a React működését. Hogyan élik át a komponensek az alkatrészek életciklusát az alkalmazások élettartama alatt? Tehát, mielőtt bármilyen optimalizálási technikába belevágnánk, jobban meg kell értenünk, hogy a React hogyan működik a motorháztető alatt.

A React középpontjában a JSX szintaxisa és a React erőteljes képessége áll a virtuális DOM-ok felépítésére és összehasonlítására. Megjelenése óta a React számos más front-end könyvtárat befolyásolt. Például a Vue.js a virtuális DOM-ok ötletére is támaszkodik.

Minden React alkalmazás egy gyökérösszetevővel kezdődik. Elképzelhetjük, hogy az egész alkalmazás egy faképződés, ahol minden csomópont egy alkotóelem. A React komponensei olyan „funkciók”, amelyek az adatok alapján jelenítik meg a felhasználói felületet. Ez azt jelenti, hogy kellékeket és állapotot kap; mondd aztCF

UI = CF(data)

A felhasználók interakcióba lépnek a kezelőfelülettel, és megváltoztatják az adatokat. Az interakciók bármit megtehetnek a felhasználó által az alkalmazásunkban. Például kattintson egy gombra, csúsztassa a képeket, húzza körbe a listaelemeket, és az AJAX-kérelmek hívják meg az API-kat. Az összes ilyen interakció csak megváltoztatja az adatokat. Soha nem okoznak változást a felhasználói felületen.

Itt az adatok mindazok, amelyek meghatározzák az alkalmazás állapotát . Nem csak az adatbázisunkban tároltakat. Még különböző kezelői állapotok, például az éppen kiválasztott lap vagy az, hogy a jelölőnégyzet be van-e jelölve, vagy sem, részei ezeknek az adatoknak. Amikor az adatok változnak, a React az összetevő függvényeit használja az UI visszaadásához, de csak virtuálisan:

UI1 = CF(data1)UI2 = CF(data2)

A React kiszámítja a jelenlegi felhasználói felület és az új felhasználói felület közötti különbségeket, összehasonlító algoritmust alkalmazva a virtuális DOM két verziójára.

Changes = Difference(UI1, UI2)

Ezután a React folytatja, hogy csak a felhasználói felület módosításait alkalmazza a böngésző valódi felhasználói felületén. Amikor az összetevőhöz társított adatok megváltoznak, a React meghatározza, hogy szükséges-e tényleges DOM frissítés. Ez lehetővé teszi a React számára, hogy elkerülje a potenciálisan drága DOM-manipulációs műveleteket a böngészőben. Ilyen például a DOM-csomópontok létrehozása és a meglévőkhöz való hozzáférés a szükségszerűségen felül.

Az összetevők ismételt megkülönböztetése és megjelenítése a React teljesítményproblémák egyik elsődleges forrása lehet bármely React alkalmazásban. A React alkalmazás felépítése, ahol a differenciáló algoritmus nem képes hatékonyan egyeztetni, ami az egész alkalmazás ismételt megjelenítését eredményezi, ami valójában pazarolt megjelenítéseket okoz, és ez frusztrálóan lassú élményt eredményezhet.

A kezdeti renderelési folyamat során a React egy ilyen DOM fát épít -

Tegyük fel, hogy az adatok egy része megváltozik. Azt akarjuk, hogy a React tegye, csak azokat az összetevőket adja vissza, amelyeket közvetlenül érint az adott változás. Esetleg hagyja ki a többi komponens megkülönböztetését is. Tegyük fel, hogy néhány adat változik komponens 2a fenti képen, és hogy az adatok adták át Ra Bmajd 2. Ha R újra renderel, akkor minden gyermekét újra rendereli, ami azt jelenti, hogy A, B, C, D, és ezzel a folyamattal a Reactus valóban ezt teszi:

A fenti képen az összes sárga csomópont megjelenítve és megkülönböztetve van. Ez pazarolt időt / számítási erőforrásokat eredményez. Ide fektetjük elsősorban optimalizálási erőfeszítéseinket. Az egyes komponensek konfigurálása csak akkor, ha csak szükséges, megjelenítésre és megkülönböztetésre. Ez lehetővé teszi számunkra, hogy visszaszerezzük ezeket a pazarolt CPU-ciklusokat. Először megvizsgáljuk, hogyan tudjuk azonosítani alkalmazásunk pazarolt megjelenítését.

Azonosítsa az elpazarolt megjelenítéseket

Ennek néhány különböző módja van. A legegyszerűbb módszer a Reagálni a fejlesztőeszközök beállításban a kiemelés frissítés opcióra váltás .

Az alkalmazással való interakció közben a frissítések színes szegélyekkel jelennek meg a képernyőn. Ennél a folyamatnál meg kell látnia az újra megjelenített összetevőket. Ez lehetővé teszi, hogy észrevegyük azokat az újratervezéseket, amelyekre nem volt szükség.

Kövessük ezt a példát.

Ne feledje, hogy amikor beírunk egy második Todo-t, az első „Todo” is villog a képernyőn minden billentyűleütéskor. Ez azt jelenti, hogy a React a bemenettel együtt rendereli újra. Ezt hívjuk „pazarolt” renderelésnek. Tudjuk, hogy felesleges, mert az első szokásos tartalom nem változott, de React ezt nem tudja.

Annak ellenére, hogy a React csak a megváltozott DOM-csomópontokat frissíti, az újrarenderelés mégis eltart egy ideig. Sok esetben ez nem jelent problémát, de ha észrevehető a lassulás, érdemes megfontolnunk néhány dolgot a felesleges megjelenítések leállításához.

A shouldComponentUpdate módszer használata

Alapértelmezés szerint a React megjeleníti a virtuális DOM-ot, és összehasonlítja a fa minden összetevőjének különbségét a kellékeinek vagy állapotának bármilyen változásával. De ez nyilvánvalóan nem ésszerű. Ahogy az alkalmazásunk növekszik, az egész virtuális DOM újrarendelésének és összehasonlításának kísérlete minden műveletnél végül lelassítja az egészet.

A React egy egyszerű életciklus-módszerrel jelzi, hogy egy alkatrésznek újrarenderelésre van-e szüksége, vagyis shouldComponentUpdateamely az újrarendelés megkezdése előtt vált be. A függvény alapértelmezett megvalósítása visszatér true.

Amikor ez a függvény bármely komponensre igazra tér vissza, akkor lehetővé teszi a renderelés differenciálási folyamat elindítását. Ez megadja a hatalmat a renderelés differenciálási folyamatának irányítására. Tegyük fel, hogy meg kell akadályoznunk egy alkatrész újrarendelését, egyszerűen vissza kell térnünk falseettől a függvénytől. Amint a módszer megvalósításából kiderül, összehasonlíthatjuk a jelenlegi és a következő támaszt és állapotot annak megállapításához, hogy szükség van-e újrarendelésre:

Tiszta alkatrészek felhasználásával

A React-en dolgozva mindenképpen tud, React.Componentde mi a helyzet React.PureComponent? Már beszéltünk a shouldComponentUpdate életciklus-módszerről, tiszta összetevőkben már létezik egy alapértelmezett megvalósítás, shouldComponentUpdate()sekély prop és állapot összehasonlítással. Tehát a tiszta komponens olyan komponens, amely csak akkor jelenik meg újra, ha props/stateeltér az előző kellékektől és állapottól .

Sekély összehasonlításban a primitív adattípusokat, például a karakterláncot, a logikai értéket, a számot értékekkel, a komplex adattípusokat, például a tömböt, az objektumot, a függvényt, referenciával hasonlítják össze

De mi van, ha van egy funkcionális hontalan összetevőnk, amelyben ezt az összehasonlítási módszert végre kell hajtanunk, mielőtt minden újrarendezés megtörténne? A React-nek van egy magasabb rendű alkatrésze React.memo. Olyan, mint React.PureComponenta funkcionális komponensek helyett osztályok.

Alapértelmezés szerint ugyanazt csinálja, mint a shouldComponentUpdate (), amely csak sekélyen hasonlítja össze a props objektumot. De ha ellenőrizni akarjuk ezt az összehasonlítást? Második argumentumként egyedi összehasonlító függvényt is megadhatunk.

Az adatok megváltoztathatatlanná tétele

Mi lenne, ha használhatnánk, React.PureComponentde még mindig hatékony módon tudnánk megmondani, hogy bármilyen komplex kellék vagy állapot, például egy tömb, objektum stb. Itt a változhatatlan adatstruktúra megkönnyíti az életet.

A megváltoztathatatlan adatszerkezetek ötlete egyszerű. Amint azt korábban megbeszéltük, komplex adattípusok esetében az összehasonlítás a referencia alapján történik. Valahányszor egy összetett adatot tartalmazó objektum megváltozik, ahelyett, hogy az adott objektumban változtatnánk, létrehozhatunk egy másolatot az objektumról a módosításokkal, amelyek új referenciát hoznak létre.

Az ES6 objektumterjesztési operátorral rendelkezik ennek megvalósításához.

Ugyanezt tehetjük a tömbök esetében is:

Kerülje az új referencia átadását ugyanazokra a régi adatokra

Tudjuk, hogy valahányszor a propsfor komponens megváltozik, újrarendezés történik. De néha propsez nem változott. Olyan módon írunk kódot, amely szerint a React szerint megváltozott, és ez újrarendelést is okoz, de ezúttal pazarolt render. Alapvetően tehát meg kell győződnünk arról, hogy más referenciát adunk át, mint a különböző adatok kellékei. Kerülnünk kell új referencia átadását ugyanazokra az adatokra. Most megvizsgálunk néhány olyan esetet, amikor ezt a problémát létrehozzuk. Nézzük meg ezt a kódot.

Itt található annak az BookInfoösszetevőnek a tartalma, ahol két komponenst renderelünk, BookDescriptionés BookReview. Ez a helyes kód, és jól működik, de van egy probléma. BookDescriptionújra megjelenít, amikor új véleményadatokat kapunk kellékként. Miért? Amint az BookInfoösszetevő új kellékeket kap, a renderfüggvény meghívásra kerül az elemfa létrehozására. A render függvény új bookállandót hoz létre, ami azt jelenti, hogy új referencia jön létre. Tehát BookDescriptionezt megkapja bookhír referenciaként, ez okozza a BookDescription. Tehát ezt a kóddarabot átalakíthatjuk ehhez:

Most a hivatkozás mindig ugyanaz, this.bookés a megjelenítés idején nem jön létre új objektum. Ez az újrarenderelési filozófia mindenre vonatkozik, propbeleértve az eseménykezelőket is, például:

Itt két különböző módszert alkalmaztunk (kötési módszerek és a nyílfüggvény használata a renderelésben) az eseménykezelő metódusok meghívására, de mindkettő új funkciót hoz létre minden alkalommal, amikor az összetevő újra megjelenik. Tehát ezeknek a problémáknak a megoldásához megköthetjük a módszert az constructorosztály tulajdonságaiban és használatával, amely még kísérleti jellegű és még nem szabványosított, de olyan sok fejlesztő már használja ezt a módszert, hogy a funkciókat más komponensekre adja át a gyártásra kész alkalmazásokban:

Csomagolás

A React belsőleg számos okos technikát alkalmaz a felhasználói felület frissítéséhez szükséges költséges DOM-műveletek számának minimalizálása érdekében. Számos alkalmazás esetében a React használata gyors felhasználói felülethez vezet, anélkül, hogy sok munkát végezne a teljesítmény optimalizálása érdekében. Mindazonáltal, ha követhetjük a fent említett technikákat az elpazarolt megjelenítések megoldására, akkor nagy alkalmazások esetén a teljesítmény terén is nagyon sima élményt kapunk.