Tepki penceresi ile büyük listeleri sanalleştirin

Çok büyük tablolar ve listeler sitenizin performansını önemli ölçüde yavaşlatabilir. Sanallaştırma bu konuda size yardımcı olabilir.

react-window, büyük listelerin verimli bir şekilde oluşturulmasına olanak tanıyan bir kitaplıktır.

react-window ile oluşturulan 1.000 satır içeren bir listeye örnek verilmiştir. Mümkün olduğunca hızlı kaydırmayı deneyin.

Bu neden yararlı?

Birçok satır içeren büyük bir tabloyu veya listeyi görüntülemeniz gerekebilir. Bu tür bir listedeki her öğenin yüklenmesi performansı önemli ölçüde etkileyebilir.

Liste sanallaştırma veya "pencereleme", yalnızca kullanıcıya görünen öğeleri oluşturma kavramıdır. İlk başta oluşturulan öğe sayısı, listenin tamamının çok küçük bir alt kümesidir ve kullanıcı kaydırmaya devam ettiğinde görünür içeriğin "penceresi" hareket eder. Bu sayede listenin hem oluşturma hem de kaydırma performansı iyileşir.

Sanallaştırılmış bir listedeki içerik penceresi
İçeriğin "penceresini" sanallaştırılmış bir listede taşıma

"Pencereden" çıkan DOM düğümleri geri dönüştürülür veya kullanıcı listede aşağı kaydırdığında hemen yeni öğelerle değiştirilir. Bu sayede, oluşturulan tüm öğelerin sayısı pencerenin boyutuna bağlı olur.

react-window

react-window, uygulamanızda sanallaştırılmış listeler oluşturmayı kolaylaştıran küçük bir üçüncü taraf kitaplığıdır. Farklı liste ve tablo türleri için kullanılabilecek çeşitli temel API'ler sağlar.

Sabit boyutlu listeler ne zaman kullanılır?

Eşit boyutlu öğelerden oluşan uzun, tek boyutlu bir listeniz varsa FixedSizeList bileşenini kullanın.

import React from 'react';
import { FixedSizeList } from 'react-window';

const items = [...] // some list of items

const Row = ({ index, style }) => (
 
<div style={style}>
     
{/* define the row component using items[index] */}
 
</div>
);

const ListComponent = () => (
  <FixedSizeList
    height={500}
    width={500}
    itemSize={120}
    itemCount={items.length}
  >
    {Row}
  </
FixedSizeList>
);

export default ListComponent;
  • FixedSizeList bileşeni, listedeki öğelerin boyutunu kontrol etmek için bir height, width ve itemSize özelliğini kabul eder.
  • Satırı oluşturan işlev, FixedSizeList öğesine alt öğe olarak iletilir. Belirli bir öğeyle ilgili ayrıntılara index bağımsız değişkeni (items[index]) ile erişilebilir.
  • Satır oluşturma yöntemine bir style parametresi de aktarılır. Bu parametre, satır öğesine eklenmelidir. Liste öğeleri, yükseklik ve genişlik değerleri satır içi olarak atanmış şekilde mutlak olarak konumlandırılır. Bu işlemden style parametresi sorumludur.

Bu makalenin başlarında gösterilen Glitch örneğinde FixedSizeList bileşeni örneği verilmiştir.

Değişken boyutlu listeler ne zaman kullanılır?

Farklı boyutlara sahip öğelerin listesini oluşturmak için VariableSizeList bileşenini kullanın. Bu bileşen, sabit boyutlu bir listeyle aynı şekilde çalışır ancak itemSize özelliği için belirli bir değer yerine bir işlev bekler.

import React from 'react';
import { VariableSizeList } from 'react-window';

const items = [...] // some list of items

const Row = ({ index, style }) => (
 
<div style={style}>
     
{/* define the row component using items[index] */}
 
</div>
);

const getItemSize = index => {
  /
/ return a size for items[index]
}

const ListComponent = () => (
 
<VariableSizeList
    height
={500}
    width
={500}
    itemCount
={items.length}
    itemSize
={getItemSize}
 
>
   
{Row}
 
</VariableSizeList>
);

export default ListComponent;

Aşağıdaki yerleşik bileşende bu bileşenin bir örneği gösterilmektedir.

itemSize öğesine iletilen öğe boyutu işlevi, bu örnekte satır yüksekliklerini rastgele belirler. Ancak gerçek bir uygulamada, her öğenin boyutlarını tanımlayan gerçek bir mantık olmalıdır. İdeal olarak bu boyutlar verilere göre hesaplanmalı veya bir API'den alınmalıdır.

Izgaralar

react-window, çok boyutlu listelerin veya tabloların sanallaştırılması için de destek sağlar. Bu bağlamda, kullanıcı yatay ve dikey olarak kaydırırken görünür içeriğin "penceresi" değişir.

Sanallaştırılmış bir ızgaradaki içerik penceresini hareket ettirmek iki boyutlu
İçeriğin "penceresini" sanallaştırılmış bir ızgara üzerinde hareket ettirmek iki boyutlu bir işlemdir

Benzer şekilde, belirli liste öğelerinin boyutunun değişip değişemeyeceğine bağlı olarak hem FixedSizeGrid hem de VariableSizeGrid bileşenleri kullanılabilir.

  • FixedSizeGrid için API yaklaşık olarak aynıdır ancak yükseklikler, genişlikler ve öğe sayılarının hem sütunlar hem de satırlar için temsil edilmesi gerekir.
  • VariableSizeGrid için, ilgili öğelere değer yerine işlevler göndererek hem sütun genişlikleri hem de satır yükseklikleri değiştirilebilir.

Sanallaştırılmış ızgara örneklerini görmek için belgelere göz atın.

Kaydırma sırasında geç yükleme

Birçok web sitesi, kullanıcı sayfayı aşağı kaydırana kadar uzun bir listedeki öğeleri yüklemeyi ve oluşturmayı bekleyerek performansı iyileştirir. Genellikle "sonsuz yükleme" olarak adlandırılan bu teknik, kullanıcı sona yakın belirli bir eşiği geçtiğinde listeye yeni DOM düğümleri ekler. Bu, bir listedeki tüm öğeleri tek seferde yüklemekten daha iyi olsa da kullanıcı ekranı o kadar kaydırırsa DOM'u binlerce satır girişiyle doldurur. Bu, stil hesaplamalarını ve DOM mutasyonlarını yavaşlatarak performansı etkilemeye başlayan aşırı büyük bir DOM boyutuna neden olabilir.

Aşağıdaki şema bu durumu özetlemeye yardımcı olabilir:

Normal ve sanallaştırılmış liste arasında kaydırma farkı
Normal ve sanallaştırılmış liste arasındaki kaydırma farkı

Bu sorunu çözmenin en iyi yolu, bir sayfada küçük bir öğe "penceresi" tutmak için react-window gibi bir kitaplığı kullanmaya devam etmek, ancak kullanıcı aşağı kaydırdığında yeni girişleri de yavaşça yüklemektir. Ayrı bir paket olan react-window-infinite-loader, bunu react-window ile mümkün kılar.

Bir üst App bileşeninde yönetilen durum örneğini gösteren aşağıdaki kod parçasını inceleyin.

import React, { Component } from 'react';

import ListComponent from './ListComponent';

class App extends Component {
  constructor
(props) {
   
super(props);
   
this.state = {
      items
: [], // instantiate initial list here
      moreItemsLoading
: false,
      hasNextPage
: true
   
};

   
this.loadMore = this.loadMore.bind(this);
 
}

  loadMore
() {
   
// method to fetch newer entries for the list
 
}

  render
() {
   
const { items, moreItemsLoading, hasNextPage } = this.state;

   
return (
     
<ListComponent
        items
={items}
        moreItemsLoading
={moreItemsLoading}
        loadMore
={this.loadMore}
        hasNextPage
={hasNextPage}
     
/>
   
);
 
}
}

export default App;

Sonsuz yükleyici listesini içeren bir alt ListComponent öğesine bir loadMore yöntemi iletilir. Bu, sonsuz yükleyicinin kullanıcı belirli bir noktanın ötesine kaydırdığında daha fazla öğe yüklemek için geri çağırma işlevi kullanması gerektiğinden önemlidir.

Listeyi oluşturan ListComponent aşağıdaki gibi görünebilir:

import React from 'react';
import { FixedSizeList } from 'react-window';
import InfiniteLoader from "react-window-infinite-loader";

const ListComponent = ({ items, moreItemsLoading, loadMore, hasNextPage }) => {
 
const Row = ({ index, style }) => (
     
{/* define the row component using items[index] */}
 
);

 
const itemCount = hasNextPage ? items.length + 1 : items.length;

 
return (
   
<InfiniteLoader
      isItemLoaded
={index => index < items.length}
      itemCount
={itemCount}
      loadMoreItems
={loadMore}
   
>
     
{({ onItemsRendered, ref }) => (
       
<FixedSizeList
          height
={500}
          width
={500}
          itemCount
={itemCount}
          itemSize
={120}
          onItemsRendered
={onItemsRendered}
         
ref={ref}
       
>
         
{Row}
       
</FixedSizeList>
      )}
  </
InfiniteLoader>
 
)
};

export default ListComponent;

Burada FixedSizeList bileşeni InfiniteLoader içine yerleştirilmiştir. Yükleyiciye atanan öğeler şunlardır:

  • isItemLoaded: Belirli bir öğenin yüklenip yüklenmediğini kontrol eden yöntem
  • itemCount: Listede bulunan öğe sayısı (veya beklenen)
  • loadMoreItems: Liste için ek verilere çözüm bulan bir promise döndüren geri çağırma işlevi

Oluşturma özelliği, liste bileşeninin oluşturmak için kullandığı bir işlevi döndürmek için kullanılır. Hem onItemsRendered hem de ref özellikleri, iletilmesi gereken özelliklerdir.

Aşağıda, sonsuz yüklemenin sanallaştırılmış bir listeyle nasıl çalışabileceğine dair bir örnek verilmiştir.

Listede aşağı kaydırırken aynı hissi alabilirsiniz ancak artık listenin sonuna yakın bir yere kaydırdığınızda rastgele bir kullanıcı API'sinden 10 kullanıcı getirme isteği gönderilir. Tüm bunlar, tek seferde yalnızca tek bir sonuç "penceresi" oluşturulurken yapılır.

Belirli bir öğenin index özelliği kontrol edilerek, yeni girişler için istek gönderilip gönderilmediğine ve öğenin hâlâ yüklenip yüklenmediğine bağlı olarak öğe için farklı bir yükleme durumu gösterilebilir.

Örneğin:

const Row = ({ index, style }) => {
 
const itemLoading = index === items.length;

 
if (itemLoading) {
     
// return loading state
 
} else {
     
// return item
 
}
};

Aşırı tarama

Sanallaştırılmış bir listedeki öğeler yalnızca kullanıcı sayfayı kaydırdığında değiştiğinden, yeni girişler gösterilmek üzereyken boş alan kısa bir süre yanıp sönebilir. Bunu fark etmek için bu kılavuzun önceki örneklerinden herhangi birine hızlıca göz atmayı deneyebilirsiniz.

react-window, sanallaştırılmış listelerin kullanıcı deneyimini iyileştirmek için overscanCount mülküne sahip öğeleri fazladan taramanıza olanak tanır. Bu sayede, görünür "pencere"nin dışında her zaman kaç öğenin oluşturulacağını tanımlayabilirsiniz.

<FixedSizeList
 
//...
 
overscanCount={4}
>
  {...}
</FixedSizeList>

overscanCount hem FixedSizeList hem de VariableSizeList bileşenlerinde çalışır ve varsayılan değeri 1'dir. Listenin büyüklüğüne ve her öğenin boyutuna bağlı olarak, birden fazla girişin aşırı taranması, kullanıcı ekranı kaydırdığında belirgin bir boşluk yanıp sönmesini önlemeye yardımcı olabilir. Ancak çok fazla girişi aşırı taramak performansı olumsuz yönde etkileyebilir. Sanallaştırılmış bir liste kullanmanın amacı, kullanıcının herhangi bir anda görebileceği giriş sayısını en aza indirmektir. Bu nedenle, aşırı taranan öğelerin sayısını mümkün olduğunca düşük tutmaya çalışın.

FixedSizeGrid ve VariableSizeGrid için, sırasıyla overscanColumnsCount ve overscanRowsCount özelliklerini kullanarak aşırı tarama yapılacak sütun ve satır sayısını kontrol edin.

Sonuç

Uygulamanızdaki listeleri ve tabloları sanallaştırmaya nereden başlayacağınızdan emin değilseniz aşağıdaki adımları uygulayın:

  1. Oluşturma ve kaydırma performansını ölçün. Bu makalede, Chrome Geliştirici Araçları'ndaki FPS ölçer'in bir listedeki öğelerin ne kadar verimli bir şekilde oluşturulduğunu keşfetmek için nasıl kullanılabileceği gösterilmektedir.
  2. Performansı etkileyen uzun listeler veya ızgaralar için react-window ekleyin.
  3. react-window'te desteklenmeyen belirli özellikler varsa ve bu işlevi kendiniz ekleyemiyorsanız react-virtualized'i kullanabilirsiniz.
  4. Kullanıcı ekranı kaydırırken öğeleri gecikmeli olarak yüklemeniz gerekiyorsa sanallaştırılmış listenizi react-window-infinite-loader ile sarın.
  5. Boş içeriklerin gösterilmesini önlemek için listelerinizde overscanCount özelliğini, tablolarınızda ise overscanColumnsCount ve overscanRowsCount özelliklerini kullanın. Çok fazla girişi aşırı taramayın. Aksi takdirde performans olumsuz yönde etkilenir.