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 faydalı 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 liste örneğini burada bulabilirsiniz. Olabildiğince hızlı kaydırmayı deneyin.

Neden faydalı oldu?

Çok sayıda satır içeren büyük bir tablo veya liste görüntülemeniz gereken zamanlar olabilir. Böyle bir listedeki her bir öğenin yüklenmesi, performansı önemli ölçüde etkileyebilir.

Liste sanallaştırma veya "pencereleme", yalnızca kullanıcının görebildiği öğelerin oluşturulmasıdır. İlk başta oluşturulan öğelerin 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, listenin hem oluşturma hem de kaydırma performansını iyileştirir.

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

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

tepki-penceresi

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 birçok temel API sağlar.

Sabit boyut listeleri ne zaman kullanılır?

Eşit boyutlarda öğ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ırları oluşturan bir işlev, FixedSizeList öğesine alt öğe olarak geçirilir. Belirli bir öğe hakkındaki ayrıntılara index bağımsız değişkeniyle (items[index]) erişilebilir.
  • Ayrıca, satır öğesine eklenmesi gereken satır oluşturma yöntemine bir style parametresi de iletilir. Liste öğeleri, yükseklik ve genişlik değerleri satır içi olarak atanacak şekilde kesinlikle konumlandırılır ve bundan style parametresi sorumludur.

Bu makalenin önceki kısımlarında gösterilen Glitch örneğinde FixedSizeList bileşeninin bir örneği gösterilmektedir.

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 bunun yerine belirli bir değer yerine itemSize önerisi için 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ştirilmiş öğede bu bileşenin bir örneği gösterilmektedir.

itemSize özelliğine iletilen öğe boyutu işlevi, bu örnekte satır yüksekliklerini rastgele hale getirir. Ancak gerçek bir uygulamada, her öğenin boyutunu tanımlayan gerçek bir mantık olmalıdır. İdeal olarak, bu boyutların verilere dayanılarak veya API'den elde edilen şekilde hesaplanması gerekir.

Izgaralar

react-window, çok boyutlu listeleri veya ızgaraları sanallaştırmayı da destekler. Bu bağlamda, kullanıcı yatay olarak ve dikey olarak kaydırdıkça görünür içeriğin "penceresi" değişir.

Sanallaştırılmış bir ızgarada içerik penceresinin taşınması iki boyutlu bir işlemdir
Sanallaştırılmış bir ızgarada içeriğin "penceresinin" taşınması iki boyutludur

Benzer şekilde, belirli liste öğelerinin boyutlarının 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 hem sütunlar hem de satırlar için yüksekliklerin, genişliklerin ve öğe sayılarının temsil edilmesi gerekir.
  • VariableSizeGrid için hem sütun genişlikleri hem de satır yükseklikleri, değerler yerine işlevlerin ilgili sahnelere geçirilmesiyle 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

Pek çok web sitesi, kullanıcı aşağı kaydırana kadar uzun bir listedeki öğeleri yükleyip oluşturmayı bekleyerek performansı iyileştirir. Genellikle "sonsuz yükleme" olarak adlandırılan bu teknik, kullanıcı ekranı sona doğru yaklaşan belirli bir eşiği geçerken listeye yeni DOM düğümleri ekler. Bu işlem, listedeki tüm öğeleri aynı anda yüklemekten daha iyi olsa da kullanıcı sayfayı kaydırarak bu sayıdan fazla satır geçmişse DOM yine de binlerce satır girişiyle doldurulur. Bu durum, stil hesaplamalarını ve DOM mutasyonlarını yavaşlatarak performansı etkilemeye başlayan aşırı büyük bir DOM boyutuna yol açabilir.

Aşağıdaki şema bunu özetlemenize yardımcı olabilir:

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

Bu sorunu çözmek için en iyi yaklaşım, bir sayfadaki öğelerin küçük bir "penceresini" korumak ve kullanıcı sayfayı aşağı kaydırırken daha yeni girişleri geç yüklemek için react-window gibi bir kitaplığı kullanmaya devam etmektir. Ayrı bir paket (react-window-infinite-loader), react-window ile bunu mümkün kılar.

Üst App bileşeninde yönetilen bir durum örneği 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 önemlidir, çünkü kullanıcı belirli bir noktayı kaydırdıktan sonra sonsuz yükleyicinin daha fazla öğe yüklemek için bir geri çağırmayı tetiklemesi gerekir.

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 sarmalanmıştır. Yükleyiciye atanan özellikler şunlardır:

  • isItemLoaded: Belirli bir öğenin yüklenip yüklenmediğini kontrol eden yöntem
  • itemCount: Listedeki (veya beklenen) öğe sayısı
  • loadMoreItems: Liste için ek verilere çözüm sağlayan bir vaadi döndüren geri çağırma.

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

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

Listenin aşağı kaydırılması da aynı şekilde hissettirilebilir ancak artık listenin sonuna doğru her kaydırdığınızda rastgele kullanıcı API'sinden 10 kullanıcı alınması için istekte bulunulur. Bunların tümü, her defasında yalnızca tek bir sonuç "penceresi" oluşturulurken yapılır.

Belirli bir öğenin index özelliği kontrol edildiğinde, daha yeni girişler için istekte bulunulmuş olup olmamasına 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, daha yeni girişler gösterilmek üzereyken boş alanlar kısa süreliğine yanıp sönebilir. Bunu fark etmek için bu kılavuzda önceki örneklerden herhangi birini hızlıca kaydırmayı deneyebilirsiniz.

react-window, sanallaştırılmış listelerin kullanıcı deneyimini iyileştirmek için overscanCount özelliğini kullanarak öğeleri aşırı taramanıza olanak tanır. Bu, her zaman görünür "pencere" dışında kaç tane öğenin oluşturulacağını tanımlamanıza olanak tanır.

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

overscanCount, hem FixedSizeList hem de VariableSizeList bileşenleri için çalışır ve 1 varsayılan değerine sahiptir. Bir listenin ne kadar büyük olduğuna ve her bir öğenin boyutuna bağlı olarak, birden fazla girişten fazla tarama yapmak, kullanıcı ekranı kaydırdığında fark edilebilir bir boş alan yanıp sönmesini önlemeye yardımcı olabilir. Ancak çok fazla girişin aşırı taranması performansı olumsuz etkileyebilir. Sanallaştırılmış bir liste kullanmanın asıl amacı, giriş sayısını kullanıcının herhangi bir anda görebileceği giriş sayısını en aza indirmektir. Bu nedenle, fazla taranan öğelerin sayısını mümkün olduğunca düşük tutmaya çalışın.

FixedSizeGrid ve VariableSizeGrid için sırasıyla, taşacak sütun ve satır sayısını kontrol etmek üzere overscanColumnsCount ve overscanRowsCount özelliklerini kullanın.

Sonuç

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

  1. Oluşturma ve kaydırma performansını ölçün. Bu makalede, Chrome Geliştirici Araçları'ndaki FPS ölçerin, 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 ürününde desteklenmeyen özellikler varsa ve bu işlevi kendiniz ekleyemiyorsanız react-virtualized kullanmayı düşünebilirsiniz.
  4. Kullanıcı ekranı kaydırırken öğeleri geç yüklemeniz gerekiyorsa sanallaştırılmış listenizi react-window-infinite-loader ile sarmalayın.
  5. Boş içeriklerin yanıp sönmesini önlemek için listeleriniz için overscanCount özelliğini ve ızgaralarınız için overscanColumnsCount ile overscanRowsCount özelliklerini kullanın. Performansı olumsuz yönde etkileyeceğinden çok fazla sayıda giriş taraması yapmayın.