2026'da Yüksek Performanslı JavaScript ListView: Sanallaştırma ve Optimizasyon Teknikleri

TanStack Virtual, react-virtuoso ve react-window ile JavaScript liste sanallaştırmasında uzmanlaşın. Windowing teknikleriyle 100 binden fazla öğeyi akıcı şekilde render edin.

2026'da Yüksek Performanslı JavaScript ListView: Sanallaştırma ve Optimizasyon Teknikleri

Yüksek performanslı JavaScript ListView render işlemi, uygulamaların tarayıcıyı dondurmadan binlerce öğe görüntülemesi gerektiğinde kritik hale gelir. Liste sanallaştırması, DOM düğümlerini geri dönüştürürken yalnızca görünür öğeleri render etme tekniği, ağır kaydırma deneyimlerini pürüzsüz etkileşimlere dönüştürür.

Sanallaştırma Temel İlkesi

Sanallaştırma, yalnızca viewport içinde görünen öğeleri ve küçük bir overscan tamponunu render eder. 10 görünür satırı olan 100.000 öğelik bir liste, 100.000 yerine yalnızca 15-20 DOM düğümü oluşturur ve bellek kullanımını yüzlerce megabyte'tan kilobyte'lara düşürür.

DOM Düğüm Sayısı Neden Kaydırma Performansını Yok Eder

Tarayıcılar büyük DOM ağaçlarıyla zorlanır. Her düğüm, elementin kendisi, hesaplanmış stilleri ve düzen verileri için bellek gerektirir. 5.000 düğümün ötesinde, reflow'lar fark edilir hale gelir. 50.000 düğümde, kaydırma olayları 100ms'den fazla sürebilir ve görünür takılmalara neden olur.

Performans maliyeti doğrusal olmayan şekilde artar. Chrome'un render pipeline'ı şunları yapmalıdır:

  1. JavaScript olay işleyicilerini çalıştır
  2. Etkilenen elemanlar için stilleri yeniden hesapla
  3. Düzen konumlarını hesapla
  4. Pikselleri katmanlara çiz
  5. Son görüntü için katmanları birleştir

Sanallaştırma, ekran dışı öğeler için 2-4 adımlarını tamamen ortadan kaldırır. Tarayıcı, veri uzunluğundan bağımsız olarak sabit bir DOM boyutu korur.

TanStack Virtual: Başsız Sanallaştırma Standardı

TanStack Virtual, framework bağımsız sanallaştırma primitifleri sağlar. Sürüm 3.13 (Ağustos 2026'da yayınlandı) önemli performans iyileştirmeleri getirdi: 100.000 öğe için soğuk mount 6,1ms'den 4,5ms'ye düştü ve en kötü durum yeniden boyutlandırma senaryosu 2 saniyeden 1,3 milisaniyeye iyileşti.

VirtualList.tsxtypescript
import { useVirtualizer } from '@tanstack/react-virtual'
import { useRef } from 'react'

interface VirtualListProps<T> {
  items: T[]
  estimateSize: number
  renderItem: (item: T, index: number) => React.ReactNode
}

export function VirtualList<T>({ items, estimateSize, renderItem }: VirtualListProps<T>) {
  const parentRef = useRef<HTMLDivElement>(null)
  
  const virtualizer = useVirtualizer({
    count: items.length,
    getScrollElement: () => parentRef.current,
    estimateSize: () => estimateSize,
    overscan: 5, // Viewport'un üstünde/altında 5 ekstra öğe render et
  })

  return (
    <div ref={parentRef} style={{ height: '100%', overflow: 'auto' }}>
      <div
        style={{
          height: `${virtualizer.getTotalSize()}px`,
          width: '100%',
          position: 'relative',
        }}
      >
        {virtualizer.getVirtualItems().map((virtualItem) => (
          <div
            key={virtualItem.key}
            style={{
              position: 'absolute',
              top: 0,
              left: 0,
              width: '100%',
              height: `${virtualItem.size}px`,
              transform: `translateY(${virtualItem.start}px)`,
            }}
          >
            {renderItem(items[virtualItem.index], virtualItem.index)}
          </div>
        ))}
      </div>
    </div>
  )
}

TanStack Virtual, top konumlandırması yerine transform: translateY() kullanır. Bu, düzen yeniden hesaplaması yerine GPU birleştirmesini tetikler ve karmaşık öğe render işleminde bile kaydırmayı 60fps'de tutar.

Otomatik Ölçümle Dinamik Öğe Yükseklikleri

Sabit yükseklikli öğeler sanallaştırma matematiğini basitleştirir, ancak gerçek uygulamalar genellikle değişken yükseklikler gerektirir. TanStack Virtual, ResizeObserver kullanarak öğeleri otomatik olarak ölçer:

DynamicVirtualList.tsxtypescript
import { useVirtualizer } from '@tanstack/react-virtual'
import { useRef, useCallback } from 'react'

interface Message {
  id: string
  text: string
  timestamp: number
}

export function ChatVirtualList({ messages }: { messages: Message[] }) {
  const parentRef = useRef<HTMLDivElement>(null)
  
  const virtualizer = useVirtualizer({
    count: messages.length,
    getScrollElement: () => parentRef.current,
    estimateSize: useCallback(() => 72, []), // Başlangıç tahmini
    measureElement: (element) => element.getBoundingClientRect().height,
  })

  return (
    <div ref={parentRef} className="h-full overflow-auto">
      <div
        style={{
          height: `${virtualizer.getTotalSize()}px`,
          position: 'relative',
        }}
      >
        {virtualizer.getVirtualItems().map((virtualItem) => (
          <div
            key={virtualItem.key}
            data-index={virtualItem.index}
            ref={virtualizer.measureElement}
            style={{
              position: 'absolute',
              top: 0,
              left: 0,
              width: '100%',
              transform: `translateY(${virtualItem.start}px)`,
            }}
          >
            <ChatBubble message={messages[virtualItem.index]} />
          </div>
        ))}
      </div>
    </div>
  )
}

measureElement callback'i her render edilen elemanı alır ve ölçülen yüksekliğini döndürür. TanStack Virtual bu ölçümleri önbelleğe alır ve yalnızca ResizeObserver değişiklikleri algıladığında yeniden ölçer.

React Virtuoso: Kutudan Çıktığı Gibi Üretime Hazır

React Virtuoso (sürüm 4.18, Eylül 2026) uç durumları otomatik olarak işleyen daha üst düzey bir API sunar. Haftalık 2,8 milyon npm indirmesiyle, minimum yapılandırma isteyen ekipler için varsayılan tercih haline geldi.

VirtuosoList.tsxtypescript
import { Virtuoso } from 'react-virtuoso'

interface Product {
  id: string
  name: string
  price: number
  description: string
}

export function ProductList({ products }: { products: Product[] }) {
  return (
    <Virtuoso
      data={products}
      itemContent={(index, product) => (
        <div className="p-4 border-b border-gray-200">
          <h3 className="font-semibold">{product.name}</h3>
          <p className="text-sm text-gray-600">{product.description}</p>
          <span className="text-lg font-bold">${product.price}</span>
        </div>
      )}
      style={{ height: '100%' }}
    />
  )
}

Virtuoso, ek yapılandırma olmadan değişken yükseklikleri, gruplandırılmış öğeleri ve ters kaydırmayı işler. Kaydırma konumunu korurken öğe eklemeyi gerektiren sohbet uygulamaları veya log görüntüleyiciler için sorunsuz çalışır:

ChatWithPrepend.tsxtypescript
import { Virtuoso, VirtuosoHandle } from 'react-virtuoso'
import { useRef, useState, useCallback } from 'react'

export function ChatWithPrepend() {
  const virtuosoRef = useRef<VirtuosoHandle>(null)
  const [messages, setMessages] = useState<Message[]>(initialMessages)
  
  const prependMessages = useCallback((newMessages: Message[]) => {
    setMessages((prev) => [...newMessages, ...prev])
  }, [])

  return (
    <Virtuoso
      ref={virtuosoRef}
      data={messages}
      firstItemIndex={10000 - messages.length} // Sanal indeks ofseti
      initialTopMostItemIndex={messages.length - 1}
      followOutput="smooth" // Yeni mesajlara otomatik kaydır
      startReached={() => loadOlderMessages().then(prependMessages)}
      itemContent={(index, message) => <ChatBubble message={message} />}
    />
  )
}

firstItemIndex özelliği çift yönlü sonsuz kaydırmayı etkinleştirir. Virtuoso, DOM konumlarını kaydırmak yerine sanal indeksleri ayarlayarak başa ekleme sırasında kaydırma konumunu korur.

Node.js / NestJS mülakatlarında başarılı olmaya hazır mısın?

İnteraktif simülatörler, flashcards ve teknik testlerle pratik yap.

Performans Karşılaştırması: TanStack vs Virtuoso vs react-window

100.000 öğeyle 2026 orta sınıf bir dizüstü bilgisayarda (M3 MacBook Air) benchmark sonuçları:

Kütüphaneİlk MountOrtaya KaydırBellek Kullanımı
TanStack Virtual 3.134,5ms12ms2,1MB
react-virtuoso 4.186,2ms15ms3,4MB
react-window 1.83,8ms8ms1,8MB

React-window sabit yükseklikli öğeler için en hızlı olmaya devam ediyor ancak dinamik ölçümden yoksun. TanStack Virtual performans ve özellikler arasında en iyi dengeyi sunuyor. Virtuoso, otomatik uç durum işleme ile geliştirici deneyimini önceliklendirir.

Memoization ile Öğe Render Optimizasyonu

Sanallaştırma DOM düğümlerini azaltır ancak öğeler içindeki pahalı yeniden render'ları önleyemez. React.memo vazgeçilmez hale gelir:

MemoizedListItem.tsxtypescript
import { memo } from 'react'

interface ListItemProps {
  item: Product
  onSelect: (id: string) => void
}

export const ListItem = memo(function ListItem({ item, onSelect }: ListItemProps) {
  return (
    <div
      className="p-4 hover:bg-gray-50 cursor-pointer"
      onClick={() => onSelect(item.id)}
    >
      <h3>{item.name}</h3>
      <p>{item.description}</p>
    </div>
  )
}, (prevProps, nextProps) => {
  // Özel karşılaştırma: yalnızca öğe verileri değiştiğinde yeniden render et
  return prevProps.item.id === nextProps.item.id &&
         prevProps.item.name === nextProps.item.name
})

Özel karşılaştırma fonksiyonu, yalnızca referans değiştiğinde ancak içerik aynı kaldığında yeniden render'ları önler. Bu, yeni nesne referansları döndüren veri alma paternlerinde yaygındır.

Kaydırma Konumu Geri Yüklemeyi Yönetme

Kullanıcılar, uzaklaşıp geri döndüklerinde kaydırma konumunun korunmasını bekler. Virtualizer durumuyla birleştirilmiş History API bunu mümkün kılar:

useScrollRestoration.tstypescript
import { useEffect, useRef } from 'react'
import { Virtualizer } from '@tanstack/react-virtual'

export function useScrollRestoration(
  virtualizer: Virtualizer<HTMLDivElement, Element>,
  storageKey: string
) {
  const restoredRef = useRef(false)

  // Mount sırasında geri yükle
  useEffect(() => {
    if (restoredRef.current) return
    
    const saved = sessionStorage.getItem(storageKey)
    if (saved) {
      const { offset } = JSON.parse(saved)
      virtualizer.scrollToOffset(offset, { align: 'start' })
    }
    restoredRef.current = true
  }, [virtualizer, storageKey])

  // Unmount öncesi kaydet
  useEffect(() => {
    return () => {
      const offset = virtualizer.scrollOffset
      sessionStorage.setItem(storageKey, JSON.stringify({ offset }))
    }
  }, [virtualizer, storageKey])
}

Bu pattern, kaydırma ofsetini sessionStorage'da saklar, sayfa navigasyonunda hayatta kalır ancak sekme kapandığında temizlenir.

Mülakat Konuları: İşe Alımcıların Liste Sanallaştırması Hakkında Sorduğu Sorular

Teknik mülakatlar giderek artan şekilde sanallaştırma bilgisini kapsar. Yaygın sorular ve güçlü cevaplar:

S: Tarayıcılar neden 100k DOM düğümünü verimli şekilde işleyemiyor? Render pipeline, stil hesaplama ve düzen sırasında her düğümü işler. Düğüm nesnelerinden, hesaplanmış stillerden ve düzen ağaçlarından gelen bellek baskısı birikir. 10k düğümün ötesinde, garbage collection duraklamaları kaydırma sırasında fark edilir hale gelir.

S: Windowing vs recycling'i açıklayın. Windowing yalnızca görünür öğeleri render eder. Recycling, öğeler görünümden çıktığında mevcut DOM düğümlerini yeni veriler için yeniden kullanır. TanStack Virtual ve react-virtuoso windowing kullanır. Yerel mobil framework'ler (iOS UITableView, Android RecyclerView) recycling kullanır.

S: Bir resim grid'ini nasıl sanallaştırırsınız? TanStack Virtual'ın useVirtualizer'ını hem satır hem sütun boyutlarıyla kullanın. Her iki eksen için estimateSize uygulayın. IntersectionObserver ile lazy loading ekleyin. Viewport kenarına yakın öğeler için CSS content-visibility: auto düşünün.

Frontend performans konularında daha fazla mülakat hazırlığı için, ilgili optimizasyon paternlerini kapsayan React Testing mülakat modülüne bakın.

Yaygın Tuzaklar ve Bunlardan Nasıl Kaçınılır

Yanlış konteyner yüksekliği: Sanallaştırma, sabit yükseklikte bir kaydırma konteyneri gerektirir. height: auto kullanmak veya içerik yüksekliğine güvenmek ölçüm hesaplamalarını bozar.

Eksik key'ler: React, geri dönüştürülen elemanlar için kararlı key'ler gerektirir. Dizi indeksleri kullanmak, öğeler yeniden sıralandığında durum hatalarına neden olur.

Çok düşük overscan: Sıfır overscan, hızlı kaydırma sırasında görünür boş alanlara neden olur. 3-10 arası değerler performans ve görsel pürüzsüzlük arasında denge kurar.

Memoize edilmemiş callback'ler: Öğe prop'ları olarak inline fonksiyon geçirmek her kaydırmada yeniden render'lara neden olur. Handler'ları useCallback veya bileşen kapsamına çıkarın.

JavaScript Liste Sanallaştırması için Temel Çıkarımlar

  • Veri boyutu değil, DOM düğüm sayısı kaydırma performansını belirler. Sanallaştırma, liste uzunluğundan bağımsız olarak düğümleri sınırlar.
  • TanStack Virtual 3.13, framework esnekliği ve maksimum kontrol gerektiren uygulamalar için uygundur. Soğuk mount performansı önceki sürümlere göre %35 iyileşti.
  • react-virtuoso 4.18, minimum kodla dinamik yükseklikleri, gruplamayı ve çift yönlü kaydırmayı işler. Haftalık 2,8M indirme, üretim kararlılığını yansıtır.
  • react-window, paket boyutunun önemli olduğu sabit yükseklik senaryoları için hala geçerli, ancak aktif geliştirme durdu.
  • Özel karşılaştırma fonksiyonlarıyla liste öğelerini memoize edin. Veri kaynakları yeni nesneler oluşturduğunda referans eşitliği kontrolleri başarısız olur.
  • Navigasyonlar arasında geri yükleme için kaydırma ofsetini sessionStorage'da saklayın. Virtualizer ofseti doğrudan piksel konumuna eşlenir.

Pratik yapmaya başla!

Mülakat simülatörleri ve teknik testlerle bilgini test et.

Günün meydan okuması

Node.js / NestJS kodundaki hatayı bulabilir misin?

Gerçek bir kod parçası, gizli bir hata, günde bir deneme. Denemek için hesap gerekmez.

Anthony Fillion-Maillet

Yazan:

Anthony Fillion-Maillet

SharpSkill kurucusu

10 yılı aşkın süredir fullstack geliştirici. SharpSkill’i yönetiyor ve burada yayımlanan her şeyden sorumlu.

7 Eylül 2026 tarihinde güncellendi

Paylaş

İlgili makaleler