REACT NATİVE PERFORMANS: BRİDGE NEDİR, JSI VE NATİVE MODULE YAKLAŞIMI
React Native ile hızlıca ürün çıkarırken performansın “bir noktada” duvara tosladığını hissetmek çok normal: listeler takılır, animasyonlar kare atlar, büyük veri akışlarında uygulama nefes nefese kalır. Bu kırılma anlarının önemli bir kısmı, JavaScript tarafı ile native taraf arasındaki köprünün (Bridge) nasıl çalıştığını anlamadan teşhis edilemez.
Bu yazıda Bridge mantığını, neden maliyet ürettiğini ve modern mimaride JSI ile birlikte nelerin değiştiğini adım adım ele alacağız. Ayrıca “hangi durumda Native Module yazmalı, hangi durumda JSI tabanlı yaklaşım seçmeli?” sorusuna, gerçekçi örnekler ve ölçüm odaklı bir bakışla yaklaşacağız.
Hedefimiz; bir yandan kavramları netleştirirken, diğer yandan pratik bir karar çerçevesi sunmak: performans darboğazını doğru yerde aramak, doğru aracı seçmek ve değişikliğin etkisini ölçerek ilerlemek.

Bridge Nedir ve Neden Performans Maliyeti Üretir?
React Native’in klasik mimarisinde JavaScript tarafındaki kod, native dünyadaki UI ve platform API’leriyle doğrudan konuşmaz. Bunun yerine arada bir “köprü” vardır: Bridge. Bu köprü, iki dünya arasındaki çağrıları genellikle asenkron ve mesaj tabanlı şekilde taşır.
Bridge’in temel fikri güvenli ve taşınabilir bir iletişim sağlamaktır; ancak bedeli, verinin paketlenmesi ve kuyruklanmasıdır. Özellikle sık çağrı yapılan senaryolarda, küçük bir gecikme bile toplamda ciddi bir maliyete dönüşebilir.
Serialization ve Kopyalama Etkisi
Bridge üzerinden geçen veri, çoğunlukla JSON-benzeri bir yapıya dönüştürülür. Bu süreçte serialization ve çoğu durumda bellek kopyalama devreye girer. Büyük payload’lar (örneğin uzun listeler, büyük objeler, base64 içerikler) bu maliyeti katlar.
Bir kez daha altını çizmek gerekir: sorun tek bir çağrının yavaş olması değil, çağrı sayısı ve taşıdığı veri büyüdükçe maliyetin doğrusal hatta bazen daha kötü artmasıdır.
Thread Hops ve Kuyruklanma
Bridge, pratikte “iki farklı iş parçacığı ve iki ayrı çalışma ortamı” arasında gidip gelmek demektir: JavaScript iş parçacığı, native modül iş parçacıkları ve UI iş parçacığı. Bu geçişler kuyruklanma yaratır; UI tarafı yoğunken JS tarafının yetişememesi veya tersi durumlar, kullanıcıya “takılma” olarak yansır.
Özellikle animasyon ve gesture yoğun ekranlarda, Bridge üzerinden sık mesaj göndermek UI akıcılığını düşürür. Burada kritik sinyal, ölçüm araçlarında görülen artan “frame time” ve düzensiz kare süreleridir.
Bridge Kaynaklı Darboğazları Nasıl Tanırsınız?
Performans sorunlarını Bridge’e bağlamadan önce semptomları doğru okumak gerekir. Çünkü her yavaşlık Bridge değildir; bazen pahalı render, bazen yanlış memoization, bazen de aşırı state güncellemesidir. Yine de Bridge kaynaklı sorunların tipik izleri vardır.
UI Jank ve Tutarsız Frame Süreleri
Kaydırma sırasında liste elemanları “dişli dişli” ilerliyorsa, animasyonlar düzenli değilse veya dokunuşa tepki gecikiyorsa, JS ile UI tarafı arasındaki senkronizasyon kırılıyor olabilir. Özellikle “bir olay tetikleniyor, ardından UI güncellemesi gecikiyor” zinciri Bridge kuyruklanmasıyla ilişkilendirilebilir.
Büyük Payload Taşıma ve GC Basıncı
Bridge üzerinden büyük veri taşımak, hem JS hem native tarafta bellek basıncını artırır. Bu durum, çöp toplama (GC) periyotlarını sıklaştırabilir. Sık GC, kısa ama hissedilir mikro-donmalara yol açar; kullanıcı bunu “arada titreme” gibi algılar.
JSI Nedir? Bridge’e Alternatif Bir Kapı
JSI (JavaScript Interface), React Native’in yeni mimarisinin temel taşlarından biridir. Amaç, JS çalışma zamanı ile native dünyayı daha doğrudan konuşturmak ve Bridge’in serialization/kuyruk maliyetini azaltmaktır.
JSI ile, JavaScript tarafı native fonksiyonlara daha “yakın” bir biçimde erişebilir. Bu yaklaşımın en büyük getirilerinden biri, bazı senaryolarda veriyi JSON’a çevirmeden ya da kuyruk mesajı gibi taşımadan, daha verimli bir erişim modeline geçebilmektir.

JS Runtime Bağımsızlığı ve Hermes Etkisi
JSI, belirli bir JS motoruna sıkı sıkıya bağlı kalmadan bir arayüz sunmayı hedefler. Pratikte Hermes kullanımı yaygındır ve performans/başlatma süresi gibi alanlarda avantajlar sağlayabilir. Yine de esas fark, JSI’nin “köprü mesajlaşması” yerine daha düşük ek yükle çalışma imkânı sunmasıdır.
Senkron Çağrılar ve Riskli Noktalar
JSI’nin bazı kullanım desenleri, senkron etkileşimlere kapı aralayabilir. Bu güçlüdür ama dikkat ister: UI iş parçacığını bloke edecek bir native hesaplama, bir anda “hızlandırma” hedefini tersine çevirebilir. Bu yüzden JSI tabanlı çözümlerde iş yükünü doğru yerde çalıştırmak ve ölçümle ilerlemek önemlidir.
// Örnek: JSI ile host function fikrini basitleştirilmiş şekilde anlatan pseudo kod
// C++ tarafında bir fonksiyon JS runtime'a bağlanır (detaylar platforma göre değişir)
createHostFunction("fastHash", (args) => {
// args[0] string olsun
const input = args[0];
// Çok hızlı native hash hesapla (örnek)
return nativeFastHash(input);
});
// JS tarafında doğrudan çağrı gibi kullanılabilir
const id = fastHash("user:1234");Native Module Yaklaşımı: Ne Zaman, Neden?
Native Module, React Native tarafında platform API’lerine erişmek için yıllardır kullanılan ana yöntemdir. Kamera, konum, dosya sistemi, sensörler gibi platform yetenekleri çoğunlukla bu yolla bağlanır. Performans açısından kritik soru şudur: modülünüz Bridge üzerinden ne kadar sık konuşuyor ve ne kadar veri taşıyor?
Bir Native Module, eğer seyrek çağrılıyorsa ve küçük payload taşıyorsa çoğu durumda yeterince iyi performans sunar. Sorun genelde “çok sık çağrı + büyük payload” kombinasyonunda ortaya çıkar.
Klasik Native Module Tasarım İlkeleri
Bridge maliyetini azaltmak için ilk adım tasarımı düzeltmektir. Örneğin, her küçük değişiklikte JS tarafına event fırlatmak yerine, değişiklikleri batch’lemek, yalnızca ihtiyaç olduğunda veri çekmek veya state senkronizasyonunu azaltmak iyi bir başlangıçtır.
- Event sayısını azaltmak ve toplu bildirim kullanmak
- Payload boyutunu düşürmek; ham veri yerine türetilmiş özet taşımak
- UI kritik akışları Bridge’e bağımlı kılmamak
- Hesaplama ağırlığını uygun iş parçacığına taşımak
TurboModules ve Codegen Mantığı
Yeni mimaride TurboModules yaklaşımı, modül çağrılarını daha verimli hâle getirmeyi hedefler. Ayrıca tip güvenliği ve otomatik binding üretimi (codegen) gibi konforlar sağlar. Bu, “modül yazma” pratiğini standartlaştırırken aynı zamanda bazı senaryolarda gecikmeyi düşürmeye yardımcı olur.
Burada önemli olan, TurboModules’un tek başına mucize olmadığıdır. Gerçek fayda, doğru sınırlar çizildiğinde ortaya çıkar: arayüzü küçük tutmak, çağrıları seyrekleştirmek ve veri akışını doğru yönlendirmek.
// Örnek: Basit bir Native Module kullanım fikri (JS tarafı)
import { NativeModules } from "react-native";
const { DeviceMetrics } = NativeModules;
async function getStartupMetrics() {
// Seyrek çağrı: uygulama açılışında bir kez
const metrics = await DeviceMetrics.getColdStartInfo();
return metrics;
}
// Örnek veri: { ttiMs: 820, jsBundleMs: 140, nativeInitMs: 260 }Hangi Yaklaşım Daha Mantıklı: Bridge, JSI, Native Module?
Seçim, “hangi teknoloji daha yeni?” sorusuyla yapılmaz. Seçim, darboğazın nerede olduğuna göre yapılır. Eğer sorun render tarafındaysa, JSI’a geçmek hiçbir şey kazandırmayabilir. Eğer sorun Bridge üzerinden taşınan veri ve çağrı sayısıysa, tasarım değişikliği veya yeni mimari yaklaşımı ciddi fark yaratabilir.
Karar Çerçevesi: Basit Bir Matriks
Aşağıdaki düşünce hattı, pratikte hızlı yön buldurur:
- Çağrı sıklığı düşük, payload küçük: Klasik Native Module çoğu zaman yeterli
- Çağrı sıklığı yüksek, payload orta/büyük: Önce API tasarımı ve batch yaklaşımı
- Performans kritik, sık etkileşimli akış: JSI tabanlı çözüm veya yeni mimari
- UI akıcılığı kritik: JS tarafı render optimizasyonu ve UI thread yükünü azaltma
Özellikle “bir sensörden saniyede onlarca veri okuyorum” veya “animasyonla birlikte sürekli native veri soruyorum” gibi senaryolarda, kuyruklanma ve kopyalama maliyeti daha görünür hâle gelir.
Anti-Pattern’ler ve Yaygın Hatalar
Performans iyileştirmesi yaparken sık düşülen bazı tuzaklar vardır:
- Ölçmeden değiştirmek: Değişiklik sonrası kazanımı kanıtlayamamak
- “Her şeyi native yapalım” refleksi: bakım maliyetini patlatmak
- Bridge üzerinden büyük objeyi sık taşımak: gereksiz serialization yükü
- UI thread üzerinde ağır hesap: akıcılığı doğrudan bozmak
İyi yaklaşım, değişikliği küçük adımlarla yapmak ve her adımın etkisini görünür hâle getirmektir. Bu, ekip içinde ortak bir performans dili oluşturur.
Profiling ve Ölçüm: İyileştirme Nerede Başlar?
Performans konuşması, ölçüm olmadan “hissettim” düzeyinde kalır. React Native ekosisteminde birkaç araç birlikte kullanıldığında, sorunun kaynağına hızlıca yaklaşılabilir. Burada önemli olan, tek bir metriğe bakmak yerine bütün sinyalleri birlikte okumaktır.

Flipper, Performance Monitor ve Log Stratejisi
Flipper üzerinden ilgili eklentilerle ağ, log ve bazı performans sinyallerini görmek mümkün olur. Bunun yanında React Native’in performance monitor çıktıları, “JS thread mi tıkandı, UI thread mi zorlanıyor?” sorusuna ilk ipuçlarını verir.
Log tarafında ise aşırı log yazımı performansı bozabilir. Bu yüzden üretim ortamında log seviyelerini kontrollü tutmak, test ortamında ise hedefli log’larla ilerlemek daha sağlıklıdır.
Systrace/Timeline Okuma ve İyileştirmeyi Kanıtlama
Zaman çizelgesi yaklaşımıyla, etkileşim anındaki thread davranışını görmek kritik avantaj sağlar. Örneğin, bir kaydırma sırasında JS thread’de uzun görevler görülüyorsa, render ve state güncellemesi tarafına bakmak gerekir. UI thread uzun görevler içeriyorsa, layout/animasyon ve native çizim yükü incelenmelidir.
Bridge kaynaklı şüphelerde, tipik olarak “JS tarafı event’leri üretiyor ama native taraf yetişemiyor” ya da “native taraftan gelen event yağmuru JS’yi boğuyor” gibi desenler yakalanır.
Gerçek Hayat Senaryoları: Örneklerle Yaklaşım Seçimi
Teoriyi pratikle bağlamak için birkaç tipik senaryoya bakalım. Her birinde amaç, teknolojiyi seçmekten önce problemi netleştirmektir.
Sürekli Sensör Okuma ve Anlık UI Güncellemesi
İvmeölçer veya konum gibi sürekli veri akışlarında, her ölçümü JS tarafına taşımak yerine veri azaltma stratejileri kullanılabilir: örnekleme aralığı düşürme, eşik tabanlı bildirim, hareketli ortalama ile gürültüyü azaltma. Böylece Bridge’e düşen çağrı sayısı azalır ve UI daha stabil kalır.
Büyük Liste ve Karmaşık Hücre Render’ları
Büyük listelerde sorun çoğu zaman Bridge değil, render maliyetidir. Burada sanal listeleme ayarları, memoization, gereksiz re-render’ları kesmek ve görsel hiyerarşiyi sadeleştirmek daha büyük kazanım getirir. Yine de hücrelerin içinde native modüllerle sık konuşma varsa, bu iletişimi azaltmak kritik olur.
Özet ve İleri Adım
Bridge, React Native’in klasik mimarisinde güvenli ve taşınabilir bir iletişim sağlarken, serialization ve kuyruklanma gibi maliyetler nedeniyle performans sorunlarına zemin hazırlayabilir. JSI, bu maliyeti azaltmaya yönelik daha doğrudan bir etkileşim katmanı sunar. Native Module yaklaşımı ise doğru tasarlandığında hâlâ çok etkili ve üretim dostudur.
En iyi sonuç, “hangi araç daha popüler?” sorusuyla değil; ölçüm, doğru teşhis ve kontrollü iyileştirme adımlarıyla gelir. Eğer bu konuları uygulamalı örneklerle derinleştirmek isterseniz React Native Eğitimi içeriğinde, performans optimizasyonunu gerçek senaryolar üzerinden ele alan modüller bulabilirsiniz.
Not: Performans çalışmalarında küçük bir değişiklik bile büyük etki yaratabilir; ancak aynı değişiklik farklı cihazlarda farklı sonuç verebilir. Bu yüzden hedef cihaz segmentiyle test etmek ve sonuçları metriklerle doğrulamak her zaman en güvenilir yaklaşımdır.


