REACT NATIVE PERFORMANS VE BRIDGE

React Native bridge mimarisi: native tarafı ile JavaScript dünyasını bağlayan köprü metaforu

Uygulama TestFlight'a çıktı, ürün ekibi mutlu. Ama kullanıcı geri bildirimleri damlamaya başladı: "ürün listesini kaydırırken takılıyor", "klavye açılırken ekran donuyor", "kamera ekranından çıkış yarım saniye sürüyor". Profiler açılıyor, JS thread temiz görünüyor, native taraf da boş. Suçlu görünmez bir yerde: iki dünyayı birbirine bağlayan köprüde. React Native'in klasik bridge mimarisi, JSON serileştirme ve asenkron mesajlaşma üzerine kuruluydu — küçük uygulamalarda fark edilmeyen bu maliyet, büyük listelerle ve sık etkileşimle birlikte gözle görülür jank'a dönüşüyor.

Klasik Bridge Neden Yavaşlar?

React Native'in ilk mimarisinde JavaScript thread, native (UI ve modules) thread'lerle doğrudan konuşamıyordu. Her çağrı şu yolu izliyordu: JS tarafında nesne hazırlanır, JSON'a serileştirilir, bridge üzerinden asenkron olarak gönderilir, native tarafta deserialize edilir ve çalıştırılır. Cevap aynı yolu tersine kat ediyor.

Bu modelin üç temel maliyeti vardı:

  • Serileştirme: Her mesaj JSON string'ine dönüştürülüyor; büyük listeler veya sık güncellenen layout değerleri CPU yiyor.
  • Asenkron iletişim: Senkron sonuç gereken durumlarda (ölçüm, layout) ek frame gecikmesi oluşuyor.
  • Tek kuyruk: Mesajlar sıraya giriyor; ağır bir batch arkasında bekleyen küçük UI güncellemesi geç ulaşıyor.

Sonuç: 16 ms'lik frame bütçesi kolayca aşılıyor, scroll'da boş kareler oluşuyor. Konuya ilişkin konunun teknik kaynakları ek bir başvuru kaynağı olarak değerlendirilebilir.

FlatList Jank'inin Anatomisi

FlatList scroll jank anatomisi: kaydırılan liste kartlarında frame düşmesi ve görünüm pencereleri

Performans şikayetlerinin büyük çoğunluğu FlatList ekranlarından gelir. Listede her satır bir View, içinde Image, Text, belki bir TouchableOpacity. Kullanıcı parmağını sürüklediğinde JS thread yeni satırların layout'unu hesaplar, props'ları bridge üzerinden iletir. Hızlı scroll'da bu hesap yetişemez ve liste boş alanlarla, sonra ani sıçramalarla doldurulur.

Sık görülen sebepler:

  1. Anonim fonksiyonlarla renderItem tanımlamak — her render'da yeni referans, satırlar gereksiz yere yeniden çiziliyor.
  2. keyExtractor'ı index'e bağlamak — sıralama değişince tüm satırlar yeniden mount oluyor.
  3. Satır içinde inline style nesnesi — sığ karşılaştırma her seferinde false dönüyor.
  4. Yüksek çözünürlüklü görsel yüklerken cache stratejisi olmaması.
  5. getItemLayout tanımlanmamış olması — kütüphane her satırın yüksekliğini ölçmek için fazladan iş yapıyor.

Bu ayarları düzeltmek bridge yükünü azaltır, ama mimari sınırı kaldırmaz. Konuyu uçtan uca ele almak için React Native eğitimi içeriğinden yararlanabilirsiniz.

JSI: Bridge'in Yerini Alan Doğrudan Köprü

JSI (JavaScript Interface), C++ tarafında tanımlı hafif bir arayüzdür. JS motoru (Hermes, JSC veya V8) ile native dünya arasındaki iletişimi JSON serileştirmesi olmadan kurar. JS tarafından çağrılan bir fonksiyon, C++ tarafındaki host object üzerinden doğrudan native koda iner.

Pratik kazanımlar:

  • Senkron çağrılar mümkün — ölçüm, layout veya animasyon değerleri için frame kaybı yok.
  • JSON marshalling kalktığı için CPU maliyeti düşüyor.
  • Büyük veri (ArrayBuffer, native nesne referansı) sıfır kopya ile geçirilebiliyor.

JSI'nin etkisini en çok hisseden alanlar: Reanimated 2/3 ile worklet'lerin UI thread'inde çalışması, MMKV gibi senkron depolama kütüphaneleri, vision-camera gibi yüksek frekanslı frame işleyicileri.

Fabric: Yeni Render Pipeline

Eski Bridge mimarisi ile yeni JSI doğrudan bağlantısının yan yana karşılaştırma şeması

Fabric, eski UIManager'ı modernleştirir. Eski mimaride shadow tree (gölge ağaç) Java/ObjC tarafında inşa ediliyordu; Fabric ile bu yapı C++ tarafına taşındı ve her platformda aynı kod paylaşılıyor.

Performans açısından üç önemli değişiklik:

  • Senkron layout: JS tarafı, ölçüm sonuçlarını bekleyebiliyor; özellikle metin tabanlı dinamik içerik için frame kaymaları azalıyor.
  • Concurrent rendering uyumu: React 18'in Suspense ve transition'ları, Fabric ile birlikte doğru çalışıyor; ağır güncellemeler arka planda hazırlanıp tek seferde yansıtılıyor.
  • Immutable shadow tree: Çift tampon prensibiyle, eski ağaç ekrandayken yenisi inşa ediliyor; çakışma kaynaklı titremeler kayboluyor.

TurboModules ve Yeni Native Modüller

Klasik native modüllerde tüm modüller uygulama açılışında yükleniyordu — başlangıç süresi şişiyor, bellek boşa harcanıyordu. TurboModules, JSI üzerinde tembel yükleme (lazy loading) sağlar: modül ilk çağrıldığında bağlanır. Codegen ile tip güvenli arayüzler üretilir, JS ve native taraf arasındaki uyumsuzluklar derleme zamanında yakalanır.

Yeni Mimariye Geçişte Pratik Yol Haritası

Mevcut bir uygulamayı yeni mimariye taşırken adım adım ilerlemek riski düşürür:

  1. Hermes'i etkinleştirin; JSI'den tam yararlanabilmek için motor seçimi belirleyicidir.
  2. Profil çıkarın — Flipper veya Perfetto ile mevcut darboğazları ölçün, körlemesine geçiş yapmayın.
  3. Kullandığınız üçüncü parti kütüphanelerin yeni mimariyle uyumlu sürümlerini kontrol edin; uyumsuz tek bir kütüphane interop katmanını tetikleyip kazancı eritebilir.
  4. Reanimated, Gesture Handler, Screens gibi temel paketleri önce güncelleyin.
  5. newArchEnabled bayrağını dev build'de açın, üç-dört sprint boyunca regresyon takibi yapın.
  6. Production'a önce düşük riskli ekranlarla, feature flag arkasında çıkın.

Detaylı uygulama örnekleri ve modül yazımı için React Native eğitim içeriğini inceleyebilirsiniz.

Ölçmeden İyileştirme Yok

Bridge'in kalktığını söylemek tek başına yetmez — kullanıcı cihazında frame süreleri düşmediyse mimari değişikliği marketing slaytında kalır. Android tarafında Systrace ve Perfetto, iOS tarafında Instruments Time Profiler, her ikisinde de Flipper'ın Hermes profiler eklentisi gerçek veriyi verir. Hedef basit: kullanıcı parmağını sürüklerken 60 FPS'lik (veya cihaz destekliyorsa 120 FPS'lik) bütçenin altında kalan kareler.

Yeni mimari sihirli değnek değil; klasik bridge'in yapısal sınırlarını kaldıran bir altyapı. Liste optimizasyonu, görsel cache, gereksiz re-render temizliği hâlâ geçerli. Ama bu temel hijyen tamamken JSI ve Fabric'e geçmek, daha önce ulaşılamayan akıcılığı erişilebilir kılar.