React Native Academy · Oppitunti

FlatListin suorituskyvyn optimointi

Käyttäkää getItemLayoutia vakiokorkuisille riveille, määrittäkää initialNumToRender ja windowSize, käyttäkää keyExtractoria oikein ja välttäkää anonyymejä funktioviittauksia renderItemissa.

Oppitunti 3/413 vaihetta

FlatListin suorituskyvyn optimointi on ilmainen React Native Academy-oppitunti CoddyKitissä. Tämä on oppitunti 3/4. Voit lukea tästä oppimispolusta kokonaan mitkä tahansa 3 oppituntia ilmaiseksi — sen jälkeen CoddyKit PRO avaa kaikki oppitunnit sekä käytännön harjoittelun sisäänrakennetulla koodieditorilla ja ympäri vuorokauden toimivalla tekoälytuutorilla. Oppitunti kuuluu React Native Academy-oppimispolkuun, ja edistymisesi synkronoituu verkon ja CoddyKit-sovelluksen välillä. React Native Academy-kurssilla on yhteensä 4 oppituntia.

Miksi FlatListin suorituskyvyllä on merkitystä

FlatList on React Native -käyttöliittymien työjuhta – lähes jokaisessa sovelluksessa on vähintään yksi luettelo. Huonosti määritetty FlatList aiheuttaa ruutujen putoamista vierityksen aikana, tyhjien solujen näkymistä kohteiden latautuessa sekä liiallista muistin käyttöä, joka voi kaataa sovelluksen vanhemmissa laitteissa.

FlatList on virtualisoitu luettelo, mikä tarkoittaa, että se renderöi vain näytöllä tällä hetkellä näkyvät kohteet sekä pienen puskurin. Haasteena on määrittää puskuri ja renderöintiputki niin, että vieritys tuntuu välittömältä eikä estä JS-säiettä.

keyExtractor-prop

keyExtractor-prop kertoo FlatListille, miten kukin kohde yksilöidään. React käyttää avaimia täsmäytyksen tehostamiseen – kun luettelon tiedot muuttuvat, React yhdistää uudet kohteet aiemmin renderöityihin komponentteihin avaimen avulla ja päivittää vain todella muuttuneet kohteet.

Käyttäkää avaimena aina vakaata ja yksilöllistä tunnistetta, kuten tietokannan tunnistetta. Älkää koskaan käyttäkö taulukon indeksiä – jos kohteita lisätään, poistetaan tai järjestetään uudelleen, indeksiin perustuvat avaimet saavat Reactin renderöimään komponentteja uudelleen tai yhdistämään ne väärin.

// ❌ Index-based keys — breaks on reorder/insert
<FlatList
  data={posts}
  keyExtractor={(item, index) => String(index)}
  renderItem={renderPost}
/>

// ✅ Stable ID-based keys
<FlatList
  data={posts}
  keyExtractor={(item) => item.id}
  renderItem={renderPost}
/>

getItemLayout vakiokorkuisille riveille

Oletusarvoisesti FlatList mittaa jokaisen renderöidyn kohteen korkeuden rakentaakseen vierityssijaintien kartan. Mittaus tapahtuu JS-säikeessä ja aiheuttaa ylimääräistä työtä. Jos kaikilla riveillä on sama kiinteä korkeus, voitte ohittaa mittauksen kokonaan getItemLayout-ominaisuudella.

Antakaa funktio, joka palauttaa kunkin kohteen korkeuden, siirtymän ja indeksin. Siirtymä on height * index. Näin FlatList voi laskea vierityssijainnit välittömästi, ja myös scrollToIndex toimii oikein.

const ITEM_HEIGHT = 72;

<FlatList
  data={contacts}
  keyExtractor={(item) => item.id}
  getItemLayout={(data, index) => ({
    length: ITEM_HEIGHT,
    offset: ITEM_HEIGHT * index,
    index,
  })}
  renderItem={({ item }) => <ContactRow contact={item} />}
/>

initialNumToRender ja windowSize

initialNumToRender määrittää, kuinka monta kohdetta renderöidään ensimmäisen alustuksen yhteydessä. Asettakaa arvoksi ilman vieritystä näytöllä näkyvien kohteiden määrä – useampien kohteiden renderöinti tuhlaa ensimmäiseen renderöintiin käytettävää aikaa. Oletusarvo on 10, mikä on rivikorkeudesta riippuen usein liian suuri tai pieni.

windowSize määrittää renderöinti-ikkunan koon näkyvän näkymäalueen kerrannaisena. windowSize-arvo 5 tarkoittaa, että FlatList renderöi näkyvän alueen ylä- ja alapuolelta 2,5 näkymäalueen korkeuden verran. Pienemmät arvot vähentävät muistinkulutusta, suuremmat arvot vähentävät tyhjien solujen välähtelyä nopeasti vieritettäessä.

<FlatList
  data={posts}
  keyExtractor={(item) => item.id}
  renderItem={renderItem}
  initialNumToRender={8}     // Render 8 items on first paint
  windowSize={5}             // Render 2.5x viewport above/below
  maxToRenderPerBatch={5}    // Render up to 5 new items per JS frame
  updateCellsBatchingPeriod={50} // Check for updates every 50ms
/>

Anonyymien renderItem-funktioiden välttäminen

renderItem-funktion määrittäminen JSX:n sisäisenä nuolifunktiona luo uuden funktioviitteen jokaisella ylätason renderöintikerralla. Tämä kumoaa FlatListin täsmäytyksen optimoinnin sekä rivikomponentille tehdyn React.memo-memoizoinnin.

Määritelkää renderItem nimetynä funktiona komponentin rungon ulkopuolella (puhtaille renderöijille) tai useCallback-hookilla komponentin sisällä, kun sen on käytettävä sulkeuman kautta tilaa tai callback-funktioita. Sama koskee myös ItemSeparatorComponent- ja ListEmptyComponent-komponentteja.

// ❌ New reference on every render
return (
  <FlatList
    data={posts}
    renderItem={({ item }) => <PostRow post={item} onPress={handlePress} />}
  />
);

// ✅ Stable reference with useCallback
const renderItem = useCallback(
  ({ item }) => <PostRow post={item} onPress={handlePress} />,
  [handlePress]
);

return <FlatList data={posts} renderItem={renderItem} />;

removeClippedSubviews suurille luetteloille

Asettamalla removeClippedSubviews={true} kerrotte natiivikerrokselle, että näkymäalueen ulkopuolella kaukana olevat näkymät irrotetaan natiivista näkymähierarkiasta, mikä vapauttaa GPU-muistia. Tämä on erityisen tehokasta Androidilla, jossa satojen näytön ulkopuolisten näkymien pitäminen liitettyinä voi heikentää vierityksen suorituskykyä.

Ottakaa tämä asetus käyttöön, kun luettelossa on yli 50–100 kohdetta. iOS:llä on pieni rajoitus: irrotetut näkymät voivat välähtää erittäin nopeasti vieritettäessä. Jos havaitsette tämän ongelman, pienentäkää sen sijaan windowSize-arvoa.

<FlatList
  data={items}
  keyExtractor={(item) => item.id}
  renderItem={renderItem}
  removeClippedSubviews={true} // Detach off-screen views from native layer
  windowSize={5}
/>

Tilan välttäminen renderItem-funktiossa

Jokaisen renderItem-kutsun tulisi olla puhdas funktio kohteen tiedoista ja vakaista callback-funktioista. Välttäkää komponentin tilan tai kontekstiarvojen lukemista renderItem-funktion sisällä, elleivät ne ole vakaita viitteitä – jokainen tilamuutos renderöi koko luettelon uudelleen.

Jos jokainen rivi tarvitsee dynaamisia tietoja (kuten tiedon siitä, onko nykyinen käyttäjä tykännyt julkaisusta), sisällyttäkää tiedot itse kohdeobjektiin tai välittäkää ne propina vakaan valitsimen kautta. Näin FlatListin täsmäytys pysyy tehokkaana.

// ❌ Reading volatile state inside renderItem
const renderItem = ({ item }) => (
  <PostRow post={item} isLiked={likedPostIds.includes(item.id)} />
);
// If likedPostIds is in state, all rows re-render on every like

// ✅ Merge liked status into the data array before passing to FlatList
const enrichedPosts = useMemo(
  () => posts.map((p) => ({ ...p, isLiked: likedPostIds.has(p.id) })),
  [posts, likedPostIds]
);

<FlatList data={enrichedPosts} renderItem={({ item }) => (
  <PostRow post={item} />
)} />

Vedä päivitettäväksi -toiminnon suorituskyky

onRefresh- ja refreshing-propit lisäävät vedä päivitettäväksi -ohjausobjektin. Asettakaa refreshing arvoon true, kun tietojen haku on käynnissä, ja takaisin arvoon false, kun haku valmistuu. Tämä näyttää natiivin RefreshControl-latausilmaisimen.

Välttäkää suorituskyvyn vuoksi setState-kutsuja, jotka aiheuttavat tarpeettomia FlatList-päivityksiä päivityksen aikana. Päivittäkää tiedot vasta haun valmistuttua, jotta FlatList tekee yhden täsmäytyskierroksen useiden välivaiheiden sijaan.

const [refreshing, setRefreshing] = useState(false);

async function handleRefresh() {
  setRefreshing(true);
  try {
    const fresh = await fetchPosts();
    setPosts(fresh); // Single state update
  } finally {
    setRefreshing(false);
  }
}

<FlatList
  data={posts}
  renderItem={renderItem}
  refreshing={refreshing}
  onRefresh={handleRefresh}
/>

Luettelon suorituskyvyn mittaaminen InteractionManagerilla

InteractionManager siirtää raskaat toiminnot suoritettaviksi vasta, kun odottavat animaatiot ja vuorovaikutukset ovat päättyneet. Tämä on hyödyllistä ladattaessa aloitusnäytön sisältöä: animoi ensin näytön siirtymä ja nouda ja piirrä raskaat luettelotiedot vasta animaation päätyttyä.

Pidä raskaat alustavat tiedonlataukset InteractionManager.runAfterInteractions-kutsun sisällä, jotta näytön siirtymät pysyvät 60 FPS:n nopeudessa. Muuten raskas ensimmäinen nouto voi estää JS-säiettä siirtymän aikana ja aiheuttaa nykivän liukuanimaation.

import { InteractionManager } from 'react-native';

useEffect(() => {
  // Defer data load until screen transition completes
  const task = InteractionManager.runAfterInteractions(async () => {
    const data = await fetchLargeDataset();
    setPosts(data);
  });

  return () => task.cancel();
}, []);

FlashList: nopeampi vaihtoehto

Shopifyn FlashList on FlatListin suoraan korvaava vaihtoehto, joka on huomattavasti nopeampi suurilla luetteloilla. Se kierrättää natiiveja solukomponentteja (samankaltaisesti kuin Androidin RecyclerView) sen sijaan, että loisi uusia komponentteja. Tämä vähentää muistin varaamista ja roskienkeräyksen kuormitusta.

Korvaa FlatList komponentilla FlashList ja anna estimatedItemSize-prop sen sijaan, että käyttäisit getItemLayout-proppia. Useimmat muut FlatListin propit ovat yhteensopivia. FlashList on suositeltava valinta luetteloille, joissa on yli 100 kohdetta.

import { FlashList } from '@shopify/flash-list';

// Install: npx expo install @shopify/flash-list

<FlashList
  data={posts}
  keyExtractor={(item) => item.id}
  estimatedItemSize={72}   // Average row height in pixels
  renderItem={renderItem}
  onEndReached={loadMore}
  onEndReachedThreshold={0.5}
/>

FlatListin profilointi JS Thread Monitorilla

Kun olet säätänyt FlatListin asetuksia, varmista parannus Performance Monitorilla (kehittäjävalikossa). Tarkkaile vierityksen aikana JS FPS -laskuria. Jos lukema pysyy lähellä arvoa 60, luettelosi piirtyy tehokkaasti. Jatkuva putoaminen alle 50 FPS:n tarkoittaa, että JS-säie on ylikuormittunut vierityksen aikana.

Yleisin jäljelle jäävä pullonkaula edellä mainittujen optimointien jälkeen on kuvien lataaminen. Suuret etäkuvat puretaan pääsäikeessä. Lisää välimuisti ja progressiivinen lataus FastImagella (react-native-fast-image), jotta kuvien purkaminen ei estä vierityksen ruutuja.

Pikatarkistus

Testaa tämän oppitunnin React Native Mobile Development -käsitteiden ymmärtämisesi.

Oppitunnin yhteenveto

Tässä oppitunnissa opit, miten getItemLayout poistaa kiinteäkorkuisten rivien ajonaikaiset korkeusmittaukset, miten initialNumToRender ja windowSize määrittävät näkymän ympärillä piirrettävien kohteiden määrän ja miksi vakaat renderItem-viittaukset ovat ratkaisevan tärkeitä FlatListin tehokkaalle täsmäytykselle. Seuraavaksi analysoimme JavaScript-paketin kokoa ja otamme käyttöön laiskan latauksen sovelluksen käynnistymisajan lyhentämiseksi.

Aloita maksutta

Opi JavaScript tekoälytuutorin avulla — ilmaiseksi

Kirjoita ja suorita oikeaa koodia selaimessa, saa välitöntä apua tekoälytuutorilta ympäri vuorokauden ja jatka siitä, mihin jäit, verkossa tai sovelluksessa.

Kurssit
30
Oppitunnit
120

Usein kysytyt kysymykset

Onko oppitunti ”FlatListin suorituskyvyn optimointi” ilmainen?

Kyllä — voit lukea täällä verkossa kokonaan ilmaiseksi mitkä tahansa React Native Academy-oppimispolun 3 oppituntia, myös oppitunnin “FlatListin suorituskyvyn optimointi”. Sen jälkeen CoddyKit PRO avaa kaikki oppitunnit sekä interaktiiviset harjoitukset sisäänrakennetulla koodieditorilla ja ympäri vuorokauden toimivalla tekoälytuutorilla. React Native Academy-kurssilla on yhteensä 4 oppituntia.

Mitä opin oppitunnilla ”FlatListin suorituskyvyn optimointi”?

Käyttäkää getItemLayoutia vakiokorkuisille riveille, määrittäkää initialNumToRender ja windowSize, käyttäkää keyExtractoria oikein ja välttäkää anonyymejä funktioviittauksia renderItemissa. Harjoittelet React Native Academy-aihetta koodilla, jonka suoritat suoraan selaimessa. Ympäri vuorokauden käytettävissä oleva tekoälytuutori vastaa kysymyksiisi oppitunnin aikana.

Tarvitsenko kokemusta aloittaakseni React Native Academy-opiskelun?

Aiempi kokemus ei ole tarpeen. CoddyKitin React Native Academy-oppimispolku sopii vasta-alkajista edistyneisiin, joten voit aloittaa tästä tai alusta ja edetä omaan tahtiisi. Tämä on oppitunti 3/4.

Kuinka kauan ”FlatListin suorituskyvyn optimointi”-oppitunnin suorittaminen kestää?

Useimmat CoddyKitin oppitunnit kestävät noin 5–10 minuuttia. Jokainen oppitunti on lyhyt ja interaktiivinen, joten edistyt tasaisesti ja voit jatkaa siitä, mihin jäit – sekä verkossa että sovelluksessa.

Voinko kirjoittaa ja suorittaa koodia tällä React Native Academy-oppitunnilla?

Kyllä. Jokainen React Native Academy-oppitunti sisältää sisäänrakennetun koodieditorin, joten voit kirjoittaa ja suorittaa oikeaa koodia suoraan selaimessa ja saada välitöntä palautetta tekoälyltä – paikallista asennusta ei tarvita.

Kaikki tämän kurssin oppitunnit

  1. Profilointi Flipperillä ja React DevToolsilla
  2. Muistiointi: React.memo, useCallback ja useMemo
  3. FlatListin suorituskyvyn optimointi
  4. Pakettikoko ja laiska lataus
← Takaisin: React Native Academy