Lazy Loading & Code Splitting

React.lazy ile component'leri sonradan yüklemek, route bazlı code splitting, named export'larla lazy kullanmak, ve koşullu lazy loading -- basit örneklerle.

İleri 11 dk
EN

Lazy Loading & Code Splitting

Şimdiye kadar yazdığımız tüm component'ler, uygulamanın İLK yüklenen JavaScript paketine (bundle) dahildi. Uygulama büyüdükçe bu paket de büyür -- kullanıcı, belki hiç ziyaret etmeyeceği sayfaların kodunu bile indirmek zorunda kalır. Bu ders, bunu önlemenin yolunu anlatıyor.

React.lazy ile Bir Component'i Sonradan Yüklemek

lazy(), bir component'in kodunu normal bir import yerine, ihtiyaç duyulduğunda indirilen AYRI bir dosya haline getirir:

import { lazy, Suspense, useState } from "react";

// lazy(), component'in KODUNU normal bir import yerine dinamik bir
// import() ile yükler -- bu dosya, uygulamanın ilk yüklenen paketine
// (bundle) DAHİL EDİLMEZ, yalnızca gerçekten gerektiğinde ayrı bir dosya
// olarak indirilir. Bu, "code splitting" dediğimiz şey.
const CourseDetails = lazy(() => import("./CourseDetails.jsx"));

function ReactLazyBasicExample() {
  const [showDetails, setShowDetails] = useState(false);

  return (
    <div>
      <button onClick={() => setShowDetails(true)}>Show Details</button>
      {showDetails && (
        // Suspense, lazy component'in kodu YÜKLENIRKEN gösterilecek bir
        // fallback UI belirtir -- kod indirilene kadar `fallback`
        // gösterilir, indirilince gerçek component render edilir.
        <Suspense fallback={<p>Loading...</p>}>
          <CourseDetails />
        </Suspense>
      )}
    </div>
  );
}

lazy(() => import("./CourseDetails.jsx")), CourseDetails'in kodunu uygulamanın ilk paketinden ÇIKARIR -- yalnızca showDetails true olduğunda indirilir. Suspense, bu indirme sırasında bir fallback göstermek için gereklidir (bir sonraki derste Suspense'e daha yakından bakacağız).

Route Bazlı Code Splitting

lazy()'nin en yaygın kullanımı, Routing dersindeki sayfaları ayrı paketlere bölmek:

import { lazy, Suspense } from "react";
import { BrowserRouter, Routes, Route } from "react-router";

// Routing dersinde her sayfayı normal import ile yüklemiştik -- gerçek bir
// uygulamada, KULLANICI O SAYFAYA GİTMEDEN, hiç ziyaret etmeyeceği
// sayfaların kodunu indirmek istemeyiz. lazy() ile her sayfayı ayrı bir
// paket haline getirip, yalnızca o route'a gidildiğinde indirebiliriz.
const CoursesPage = lazy(() => import("./CoursesPage.jsx"));
const AboutPage = lazy(() => import("./AboutPage.jsx"));

function RouteBasedCodeSplittingExample() {
  return (
    <BrowserRouter>
      {/* Suspense, Routes'un DIŞINA sarmalanıyor -- hangi sayfaya
          gidilirse gidilsin, o sayfanın kodu yüklenene kadar TEK bir
          fallback gösterilir. */}
      <Suspense fallback={<p>Loading page...</p>}>
        <Routes>
          <Route path="/courses" element={<CoursesPage />} />
          <Route path="/about" element={<AboutPage />} />
        </Routes>
      </Suspense>
    </BrowserRouter>
  );
}

Her sayfa (CoursesPage, AboutPage) kendi ayrı dosyası -- kullanıcı /about'a hiç gitmezse, o sayfanın kodu hiç indirilmez. Bu desene code splitting (kod bölme) denir: uygulamanın tek bir dev paket yerine, birden fazla küçük parçaya bölünmesi.

Named Export'larla lazy Kullanmak

lazy(), import()'un bir DEFAULT export döndürmesini bekler -- named export'lu bir component için küçük bir uyarlama gerekir:

import { lazy, Suspense } from "react";

// lazy(), import() fonksiyonunun DEFAULT export döndürmesini bekler --
// eğer CourseChart yalnızca bir named export ise (`export function
// CourseChart() {}`, `export default` DEĞİL), .then() ile onu bir
// "default" alanına sarmalamamız gerekir.
const CourseChart = lazy(() =>
  import("./CourseChart.jsx").then((module) => ({ default: module.CourseChart })),
);

function NamedExportLazyExample() {
  return (
    <Suspense fallback={<p>Loading chart...</p>}>
      <CourseChart />
    </Suspense>
  );
}

.then((module) => ({ default: module.CourseChart })), named export'u (CourseChart) lazy'nin beklediği { default: ... } şekline DÖNÜŞTÜRÜYOR.

Koşullu Lazy Loading

lazy(), yalnızca sayfalar için değil, nadiren kullanılan HERHANGİ bir component için faydalı:

import { lazy, Suspense, useState } from "react";

// EmojiPicker gibi büyük, nadiren kullanılan bir component'i lazy
// yapmak özellikle faydalı -- kullanıcıların çoğu belki hiç açmaz, o
// zaman kodunu hiç indirmemiş oluruz.
const EmojiPicker = lazy(() => import("./EmojiPicker.jsx"));

function ConditionalLazyLoadExample() {
  const [showPicker, setShowPicker] = useState(false);

  return (
    <div>
      <button onClick={() => setShowPicker(!showPicker)}>
        {showPicker ? "Hide" : "Show"} Emoji Picker
      </button>
      {/* EmojiPicker'ın kodu, `showPicker` İLK KEZ true olana kadar HİÇ
          indirilmez -- yalnızca gerçekten kullanılacaksa yükleniyor. */}
      {showPicker && (
        <Suspense fallback={<p>Loading emoji picker...</p>}>
          <EmojiPicker />
        </Suspense>
      )}
    </div>
  );
}

EmojiPicker'ın kodu, kullanıcı showPicker'ı İLK KEZ true yapana kadar hiç indirilmez -- kullanıcıların çoğu belki hiç kullanmaz, o zaman kodunu hiç indirmemiş oluruz.

Özet ve Terimler Sözlüğü

lazy(), bir component'in kodunu ayrı bir dosyaya (chunk) bölüp, yalnızca gerçekten gerektiğinde indirir -- bu, uygulamanın ilk yüklenen JavaScript miktarını AZALTIR. En yaygın kullanımı, sayfaları (route'lar) ya da nadiren kullanılan component'leri (modal'lar, emoji picker'lar gibi) bölmek. lazy() her zaman bir Suspense ile birlikte kullanılır -- kod indirilirken gösterilecek bir fallback gerekir.

Terimler Sözlüğü

Code Splitting (Kod Bölme) — Bir uygulamanın JavaScript'ini tek bir büyük paket yerine, ihtiyaç duyuldukça indirilen küçük parçalara bölme tekniği.

Bundle (Paket) — Bir uygulamanın, tarayıcıya gönderilmek üzere birleştirilmiş JavaScript dosyası.

Chunk (Parça) — Code splitting sonucu oluşan, ayrı ayrı indirilebilen küçük bir JavaScript dosyası.