React ile uygulama geliştirmek oldukça kolaydır, ayrıca bir UI uygulamaları yazmak için geliştirdiği için arayüz performansı oldukça iyidir. Bununla birlikte uygulamalar geliştikçe performans kaybı yaşadıkları gibi, yanlış kodlamanın da performans kaybına neden olduğu biliyoruz.
Eğer React performans optimizasyonu ile ilgileniyorsak farklı bakış açılarına sahip olmak gerekecektir. Ne demek istiyorum, kodlama açısında, derleme açısından, uygulamanın yüklenmesi açısında, geliştirme mantığı açısından farklı bakış açıları geliştirilmelidir. Aslında genelde basit bir uygulamada performans sorunları çok da görülmez, ama uygulama geliştikçe yeni yeni modüller eklendikçe, kod boyutu arttıkça performans sorunları da baş göstermeye başlar.
React performans optimizasyonu için aşağıdakilerden hangisini kullanır?
Evet genellikle merak edilen bir sorudur, React performans optimizasyonu için hangisini kullanmak gerekir. Aslında yapacağınız işe bağlı olarak daha önce belirttiğimiz gibi hangi aşamada bir optimizasyona ihtiyacınız var? Kodunuz mu çok büyüdü? Çok fazla paket mi kullanıyorsunuz? Render sorunlarınız mı var? gibi birçok soru React performans optimizasyonu için aşağıdakilerden hangisini kullanır? sorusunun cevabı aranırken sorulması gereken sorulardır. Şimdi performans optimizasyonu için elimizdeki seçenekleri inceleyelim.
Gereksiz Yeniden Render İşlemlerini Engellemek
React arayüz uygulama geliştirmesini kolaylaştırırken, aynı zamanda performans artışı için sanal DOM kullanmakta. Sanal DOMun (virtual DOM) nasıl çalıştığı bilinirse performans optimizasyonu da o kadar kolay yapılacaktır.
Sanal DOM (virtual DOM) Nasıl Çalışır?
Genel olarak React tarayıcı tarafından oluşturulan DOM (Object Document Model) nesnesinin bir kopyasını sanal olarak oluşturur, buna sanal DOM (virtual DOM) diyoruz. Sonrasında eğer sanal DOM üzerinde bir değişiklik olursa, değişiklik olan bölümü gerçek DOM üzerinde değiştirir. Böylece tüm sayfa (belge) yerine sadece değişiklik yapılan yer güncellenerek bir performans artışı ortaya çıkar.
Bir komponentin yeniden render edilmesi için ya state yada komponente verilen proplarda değişiklik olması gereklidir. Mesela state üzerine score değişkeni oluşturalım ve bunu komponentimizin bir yerinde yazdıralım.
const [score, setScore] = useState(0);
return (
<button>Skor: {score}</button>
)
Bu bileşen çağrıldığında ekrana Skor: 0 yazdıracaktır. Eğer biz bir şekilde score değişkenini değiştirirsek, o zaman komponent yeninden render edilecek ve ekrana yeni skor yazılacaktır. Bunun için bir onClick eventi oluşturalım.
const [score, setScore] = useState(0);
const handleClick = (e) => {
e.preventDefault();
setScore(score + 1);
}
return (
<button onClick={(e) => handleClick(e)}>Skor: {score}</button>
)
Bu komponent render edildikten sonra her butona tıklandığında score değişkeninin değeri bir (1) artacak ve bu değişken state üzerinde tanımlandığı için komponent yeniden render edilecektir.
Bunun gibi eğer bir komponente bir prop geçiyorsa bu probun değeri değiştiğinde komponent yeniden render edilecektir.
Örnek olarak
import React from 'react';
function Uye({isim}) {
return (
<div>Üye: {isim}</div>
)
}
export default Uye;
Uye komponentine verilen isim probu değiştikçe komponent yeniden render edilecektir. Mesela isim olarak Hasan girildi ise ekrana Hasan yazılacak, sonra isim Ahmet olarak değiştiğinde komponent yeniden render edilecek ve ekrana Ahmet yazılacaktır.
Virtul DOM Performans Artırmak İçin Nasıl Kullanılır?
Öncelikle bazı ufak teknikler ile UI üzerinde neler yapabileceğimiz konusunda değinelim.
React.Fragment Kullanmak
İlk ve en basit yöntem React.Fragment kullanmak. Bildiğiniz gibi her React komponenti bir etiket içerisinde olmalıdır. Genellikle bunun için bir div etiketi kullanılır. Örneğin
return (
<div>
<h1>Üye Bilgileri</h1>
<span>İsim: </span> {isim}
</div>
)
şeklinde komponentimizin return bölümü olsun. Burada eğer kapsayıcı div etiketini kullanmazsak hata alırız. Ama div etiketine ihtiyacımız yoksa ve kullanırsak DOM üzerine gereksiz bir yük bindirmiş oluruz. Bu durumda React.Fragment yardımızıza koşuyor ve hem tüm bileşeni sarıyor ve hem de DOM üzerinde yeni bir element oluşturmuyor.
import React from 'react';
return (
<React.Fragment>
<h1>Üye Bilgileri</h1>
<span>İsim: </span> {isim}
</React.Fragment>
)
Bunun kısa yolu ise
import React from 'react';
return (
<>
<h1>Üye Bilgileri</h1>
<span>İsim: </span> {isim}
</>
)
şeklindedir.
Gereksiz Satıriçi Fonksiyonlar Kullanmamak
Bir diğer yöntem ise return içerisinde gereksiz satır içi fonksiyonlar kullanmamak. Eğer bir kompenentin return bölümünde satır içi bir fonksiyon tanımlarsanız, her render işleminde bu fonksiyon yeniden oluşturulacaktır.
import React from 'react';
function User({user}) {
return (
<div onClick={()=> console.log(user.name, user.age)}>
User Name: { user.name }
</div>
);
export default Users;
}
Yukarıdaki gibi kullanıcı seçmek için inline bir fonksiyon kullanmak yerine bu işlemi daha öncesinden bir fonksiyon olarak tanımlayarak fonksiyonun adresini referans olarak onClick eventine geçmek daha doğru olacaktır. Yani parantez kullanmadan sadece fonksiyon ismi ile kullanacağız.
import React from 'react';
function User({user}) {
const handleUser = () => {
console.log(user.name, user.age);
}
return (
<div onClick={handleUser}>
User Name: { user.name }
</div>
);
export default Users;
}
Böyle bir kullanımda fonksiyon return dışında oluşturulduğu için yeniden render işlemlerinde tekrardan oluşturulmayacaktır.
Class Komponentler Yerine Fonksiyon Komponent Kullanma
Eğer state yönetimi olmayacaksa bir fonksiyon tanımı yaparken class komponentler yerine fonksiyon komponent kullanmak performans açısından daha iyi olacaktır.
Komponenti Ufak Komponentlere Bölmek
Bileşen yapısı React ile geliştirme yapmayı kolaylaştırır. Bununla birlikte büyük komponetler yeniden render edildiğinde içindeki diğer komponetleri de yeniden render etmeye zorlar. Bu nedenle eğer büyük bir komponent içerisinde sadece ufak bir alan belli durumlarda değişiyorsa o bölümü bir komponent haline getirebiliriz.
Mesela bir oyun ekranımız olsun ve her başarılı işlem için oyuncunun puanı artırılsın. Puanı ekranda göstermek zorundayız. Eğer skoro büyük bir komponent içerisinde göserirsek, her skor değiştiğinde o komponent yeniden render edilecektir. Oysa biz skor bölümünü ayrı bir komponent yaparsak, her skor değişikliğinde sadece skor bölümü yeniden render edilecektir. Bu da bir performans artışı demektir.
Listeleme İşlemlerinde Map İndeksini Kullanmaktan Kaçınmak
React listelenen elemanları rahat denetleyebilmek için onlara bir key propu geçilmesini ister. Bu key değeri her liste elemanı için farklıdır. Mesela renkler listemiz olsun ve listeleyelim.
const colors = ['blue', 'red', 'white', 'black', 'yellow'];
return (
<div>
{
colors.map((color, index) => <span key={index}>{color}</span>)
}
</div>
);
Yukarıda görüldüğü gibi map metodundan bir index değeri alınır ve listeleme yapılan elemanlara key propu ile atanır. Bu basit bir yöntemdir ve eğer farklı map işlemleri varsa key değerlerinin çakışması muhtemeldir. Bu da bazı render sorunlarını beraberinde getirebilir. Bunu engellemek için farklı yöntemler kullanılabilir. Mesela her farklı liste için bir ön ek kullanılabilir.
colors.map((color, index) => <span key=`color_${index}`>{color}</span>)
Yukarıdaki örneğimizde renk listemiz için key değerine bir ön ek olarak color_ verdik. Böylece index değerileri color_0, color_1, color_2 gibi gidecektir. Yine eğer aynı yerde renk listesi yerine isim listesi olursa onlara da name_ ön eki getirebiliriz ve böylece keyler name_0, name_1 gibi gideceğinden key değerlerinde bir çakışma olmayacaktır.
Ayrıca shortid, nanoid gibi benzersiz ID değerleri üreten paketlerden de faydalanabiliriz.
Etiket ve Komponentler İçin Girilen Özelliklerin Kesin Değerler Olmalı
Spread (…) işlemi ile bir nesne içerisindeki tüm özellikleri bir etikete atayabiliriz.
const product = { name: "Product Name", productId: 1315566, barcode: 12242422, price: 34};
return(
<div {...product}> {product.name} : {product.price}tl </div>
)
Kullanıma dikkat ettiyseniz, product içerisinde olan name, productId, barcode ve price özellikleri div etiketine birer özellik olarak atanacaktır. Oysa biz burada sadece veri amaçlı olarak productId ve barcode değerlerini atamak istiyoruz.
Bu kullanımda gereksi özellikler div etiketice geçtiğinden fazladan bir yük getirecektir. Bunun yerine isim belirterek kulanmak çok daha uygundur.
const product = { name: "Product Name", productId: 1315566, barcode: 12242422, price: 34};
return(
<div productId={product.productId} barcode={product.barcode}>
{product.name} : {product.price}tl
</div>
)
React Performans Optimizasyonu İçin Kullanılacak Paket ve İşlemler
Programla ve DOM açısından bir miktar incelediğimiz optimizasyonlar için React performans optimizasyonu için hangileri kullanılır? sorusuna bir cevap bulmaya çalışalım. İlk başta belirttiğimiz gibi optimizasyon işlemleri için farklı aşamalarda farklı işlemler gerekli olacaktır.
React Uygulama Kodlarını Birkaç Farklı Dosyaya Ayırmak
React uygulaması geliştikçe hem kodlar artacak ve hemde kullanacağımız ikinici üçüncü parti paketler uygulamaya eklenecektir. Bu nedenle tüm bu kodların aynı dosya içerisinde olması dosya boyutunu büyütecek ve uygulamanın yüklenme ve ilk çalışma zamanını uzatarak bir performans kaybına neden olacaktır. Bu nedenle uygulama kodlarının farklı dosyalara bölünmesi faydalı olacaktır.
Bunun için farklı paketler kullanılabilir. CommonsChunkPlugin paketini kullanarak uygulamayı app.bundle.js ve vender.bundle.js şeklinde farklı dosyalara bölebiliriz.
Webpack İle Production Özelliğini Kullanmak
webpack 4 ve üzeri kullanırken uygulama modunu production yaparak gereksiz kodların uygulamadan çıkarılmasını sağlayabiliriz. React geliştirme modunda çok sayıda hata mesajı barındırdığı için bu kodun büyümesine neden oluyor. Ama geliştirme modundan kullanım (production) moduna geçildiğinde bir çok gereksiz kod uygulamadan kaldırılacaktır. Bunun için
module.exports = {
mode: 'production'
};
şeklinde mod bilgisi verilmesi gereklidir. Ayrıntılı bilgi için webpack sayfasına bakılabilir.
Eğer create-react-app ile uygulama geliştiriyorsanız production modunu kullanmak için basitçe
npm run build
şeklide komut vermeniz yeterlidir. Canlıya çıkacak kodu oluşturarak bulild klasörüne kayıt edecektir.
Paket Bağımlılığı (depencies)
Kullandığımız JavaScript paketleri farklı paketlere bağımlı olabilirler. Ayrıca bir paket içerisinde ufak bir bölümü kullanmamız gerekebilir. Bu gibi durumlarda gereksiz bir çok paketin veya dosyanın uygulamamıza eklenmesi mümkündür. Bu da bir performans kaybına neden olabilir. Bu nedenle kullanacağımız paketleri seçerken dikkat etmemiz ve eğer paketleri bölmemiz mümkünse o paketler içerisinde bize lazım olan bölümleri almamız doğru olacaktır. Bu konu ile ilgili webpack-libs-optimizations sayfasını inceleyebilirsiniz.
Komponentleri Hafızaya Alma (memoization)
Diğer bir performans artırıcı özellik ise fonksiyon ve komponentleri hafızaya almaktır. Bu sayede bir fonksiyon veya komponent yeniden çağrıldığında direkt hafızaya kayıt edilmiş bir örneği alınarak işleme konur. Bu da bir performans artışına neden verecektir. Bunun için React.memo metodu veya useMemo hooku kullanılabilir. Hafızaya alma işleminde dikkat edilmesi gereken husus, hafızaya alırken harcayacağımız kaynakların yeniden oluşturulması için gerekli kaynaklardan çok daha az olmasıdır.
React.memo dışında farklı hafızalama paketleri de bulunmaktadır.
CDN Kullanma
Yine uygulama geliştirirken eğer ihtiyaç varsa CDN kullanmak da performans artışına yardımcı olabilir. Özellikle dosyaların hızlı yüklenmesi ve farklı coğrafyalarda aynı hızda açılan sayfalar oluşturması açısında bir CDN servisi kullanmak düşünülebilir. Günümüzde çok sayıda ücretli ve ücretsiz CDN servisi bulunmaktadır. Çoğu paketler uygulamaya eklenmeden sadece CDN linkinin eklenmesi ile de kullanılabilir durumdadır.
CPU Ağırlıklı İşlemler İçin Web Workers Kullanma
Web Workers, uygulamadaki CPU ağırlıklı işlemleri hızlandırmak için geliştirilmiştir. Eğer CPU ağırlıklı işlemler uygulama içerisinde mevcutsa kulanmak bir seçenektir. Ayrıntılı bilgi için Using Web Workers yazısı incelenebilir.
Uzun Listeleri Sanallaştırma
Eğer elinizde çok uzun bir liste varsa bunun hepsini render etmek performans açısından sorunlara neden olabilecektir. Bunun için bu listeleri sanallaştırmak ve sadece görünen kısımları render etmek performans açısından oldukça faydalı olacaktır. Bu işleme virtualization (sanallaştırma) veya windowing (pencereleme) adı verilmektedir. react-window ve react-virtualized paketlerini bu iş için kullanabilirsiniz.
Sunucu Tabanlı Render Yapmak
Eğer uygulama uygunsa render işlemini sunucu tabanlı yapmak performans artışına neden olabilir. Bu işlemi React ile yapabilseniz de bir react frameworku olan Next JS bunu sizin için kolayca yapacaktır.
Sunucu Üzerinde GZIP Sıkıştırması Yapmak
Yazdığımız kodları sıkıştırarak (zip) tarayıcıya gönderirsek bu bize bir performans artışı olarak dönecektir. Bunu sunucu üzerinden GZIP özelliğini etkinleştirerek kolaylıkla yapabilirsiniz. Kullanacağınız sunucu ve paketlerin özelliklerini araştırarak bu konuda yardım alabilirsiniz.
Son Sözler
Uygulama geliştirirken performans sorunları yaşamak mümkündür. Bu sorunları aşmak için de yapılacak farklı işlemler vardır. Öncelikle program geliştirirken bazı iyileştirmeler yapmak gerekli olabilir. Bu yazımızda bazı ipuçlarını verdik. Yine programlama haricinde paketleme işlemi yaparken de ufak dokunmalar ile daha performanslı bir uygulamaya sahip olabiliriz. Ayrıca Web Workers, CDN, sunucu üzeri GZIP işlemleri gibi farklı araçlar da kullanarak performans artışına katkıda bulunabiliriz.
Bu nedenle bir geliştirici, gündemi takip etmeli ve öncelikle kensinini devamlı geliştirmelidir.
Sizin React uygulamalarında yaptığınız performans iyileştirmeleri nelerdir? Bizimle pyalaşır mısınız?
