WisarWisar
Dasturlash kitobi/11-QISM — React37 daqiqa

11.8-bob: React.lazy, Suspense va code splitting

11-QISM — Frontend: React · 8-mavzu


1. Kirish va motivatsiya

11.1–11.7-boblarda React'ning yadrosini — komponentlar, state, hooklar — chuqur o'rgandik. Ilovamiz endi interaktiv va kuchli. Lekin ilova o'sgani sayin yangi muammo paydo bo'ladi: kod hajmi (bundle) kattalashadi. 50 ta sahifa, 200 ta komponent, og'ir kutubxonalar (grafik chizish, matn muharriri, xarita) — bularning hammasi bitta katta JavaScript faylga jamlansa (11.3: 2.6), foydalanuvchi saytni birinchi marta ochganda butun ilovani (har bir sahifa, hatto u hech qachon ochmaydigan sahifalar ham) yuklab olishi kerak bo'ladi. Natija — sekin boshlang'ich yuklanish, oq ekran, sabri tugagan foydalanuvchi. Bu — SPA modelining (11.3: 2.10) eng katta zaifligi.

Yechim — code splitting (kodni bo'laklarga bo'lish): butun ilovani bitta ulkan fayl o'rniga, kichik bo'laklarga (chunk) bo'lib, har birini faqat kerak bo'lganda yuklash. Foydalanuvchi bosh sahifani ochganda — faqat bosh sahifa kodi yuklanadi (tez); "Sozlamalar" sahifasiga o'tganda — o'sha paytda sozlamalar kodi yuklanadi. Bu g'oyani React ikki vosita bilan amalga oshiradi: React.lazy — komponentni "dangasa" (kerak bo'lganda) yuklaydi, va Suspense — komponent yuklanayotgan paytda zaxira UI (spinner, skeleton) ko'rsatadi. Bularning poydevorida — JavaScript'ning dinamik import() imkoniyati yotadi (2.14-bobda modullar bilan tanishganmiz, endi uning dinamik shaklini ko'ramiz).

Bu bob: muammo (bundle hajmi va boshlang'ich yuk), code splitting nima (chunk, on-demand yuklash), dinamik import() (poydevor), React.lazy (komponentni dangasa yuklash), Suspense (fallback UI), route-based splitting (eng keng ishlatiladigan — har sahifa alohida chunk — 11.9), component-based splitting (og'ir komponentlar — grafik, modal), error boundary bilan (yuklanish xatosini ushlash), loading holatlari (spinner vs skeleton), preload/prefetch (oldindan, fonda yuklash), named export bilan lazy, Suspense data fetching uchun (React 18+/19 use()), nimani bo'lish (bundle analiz, o'lchov), va SSR/Next.js konteksti 13.10-bob. Bularning barchasi to'liq, amaliy va zamonaviy (React 19) holatda ochib beriladi.

O'xshatish: Code splitting — bu restoran menyusi va oshxona. Code splitting'siz ilova — bu mehmon kelishidan oldin oshpaz menyudagi barcha taomlarni pishirib qo'ygan restoran: mehmon eshikdan kirsa, 200 ta tayyor taom uni kutib turibdi — bu juda ko'p vaqt, joy va sovub ketgan taomlar (sekin boshlang'ich yuk). Aqlli restoran — mehmon buyurtma berganda pishiradi: lazy loading. React.lazy — "bu taomni faqat buyurtma kelsa pishir" degan ko'rsatma. Suspense — "taom pishayotganda mehmonga non va choy (fallback — spinner) ber, zerikmasin". Preload — "doimiy mijoz odatda osh buyurtma qiladi — u eshikka yaqinlashganda oshni oldindan boshla" (hover'da yuklash). Natija — mehmon tez o'tiradi (tez boshlang'ich yuk), faqat yeydigan taomi pishiriladi (kerakli kod).

Nega muhim?

  • Boshlang'ich tezlik — kichik birinchi yuk = tez ochilish = kam tark etish (bounce rate); to'g'ridan-to'g'ri biznes.
  • Katta ilova shart — 10+ sahifali ilovada code splitting'siz yuklanish chidab bo'lmas darajada sekin.
  • Performance yadrosi — 11.11 (optimizatsiya) va Core Web Vitals (LCP — 13.8 SEO)ning asosiy qismi.
  • Zamonaviy standart — React Router, Next.js — hammasi code splitting'ga tayanadi (avtomatik yoki qo'lda).

2. Nazariya — chuqur tushuntirish

2.1. Muammo — bundle hajmi va boshlang'ich yuk

text
  CODE SPLITTING'SIZ — butun ilova BITTA katta faylda (11.3: 2.6):

  foydalanuvchi saytni ochadi
       │
       ▼
  bundle.js (2 MB) ni TO'LIQ yuklab oladi   barcha sahifa, komponent, kutubxona
       │                                      (hatto ochmaydigan sahifalar ham!)
       ▼ (sekin — 5-10 soniya 3G'da)
  endi ilova ko'rinadi (oq ekran tugadi)

  ┌────────────────────────────────────────────────────────────┐
  │ Bosh sahifa: 50 KB kerak                                    │
  │ LEKIN foydalanuvchi: 2 MB yuklaydi (admin panel, grafik,    │
  │   muharrir, 50 sahifa — hammasi BIRGA, kerak bo'lmasa ham)  │
  └────────────────────────────────────────────────────────────┘

  OQIBAT:
  - Sekin boshlang'ich yuk (oq ekran, kutib turish)
  - Foydalanuvchi tark etadi (har 1s kechikish — bounce )
  - Yomon Core Web Vitals (LCP — 13.8 SEO)

   Foydalanuvchi KO'RADIGAN qismgina yuklansa — tezroq (qolgani — kerak bo'lganda)

Muammo — bundle hajmi — code splitting'ning sababini tushunish. Code splitting'siz, Vite/Webpack butun ilovani (barcha sahifa, komponent, kutubxona) bitta katta JavaScript faylga (bundle) jamlaydi (11.3: 2.6). Foydalanuvchi saytni birinchi marta ochganda, brauzer bu butun faylni (masalan 2 MB) yuklab olishi, tahlil qilishi va bajarishi kerak — garchi u faqat bosh sahifani (50 KB) ko'rmoqchi bo'lsa ham. Ya'ni admin panel, grafik kutubxonasi, matn muharriri, 50 ta sahifa — hammasi birga yuklanadi, foydalanuvchi ularning ko'rini hech ochmasa ham. Oqibat: sekin boshlang'ich yuklanish (oq ekran, kutib turish — ayniqsa sekin internetda), foydalanuvchining sabri tugab tark etishi (har soniya kechikish bounce rate'ni oshiradi), va yomon Core Web Vitals (LCP — Largest Contentful Paint — 13.8 SEO). Asosiy g'oya: foydalanuvchi hozir ko'radigan qismgina yuklansa, ilova ancha tezroq ochiladi — qolgani (boshqa sahifalar, og'ir komponentlar) faqat kerak bo'lganda yuklanadi. Bu — code splitting'ning butun mohiyati.

2.2. Code splitting nima — chunk va on-demand yuklash

text
  CODE SPLITTING — bitta katta bundle'ni kichik BO'LAKLARGA (chunk) bo'lish:

  ESKI (bitta fayl):           YANGI (bo'laklarga bo'lingan):
  bundle.js (2 MB)             main.js (50 KB)        darrov (bosh sahifa)
                               home.chunk.js (30 KB)   bosh sahifa
                               admin.chunk.js (400 KB) FAQAT admin ochca
                               editor.chunk.js (600KB) FAQAT muharrir kerak bo'lsa
                               chart.chunk.js (300 KB) FAQAT grafik ko'rsatilsa

  YUKLANISH (on-demand — kerak bo'lganda):
  Bosh sahifa ochildi  main.js + home.chunk.js (80 KB — tez!)
  Admin'ga o'tildi     admin.chunk.js O'SHA PAYTDA yuklanadi (kechikish bir marta)
  Grafik ko'rsatildi   chart.chunk.js yuklanadi

   Har chunk — alohida fayl (brauzer keshlaydi — qayta yuklanmaydi — 10.2)
   Vite/Webpack chunk'larni AVTOMATIK yaratadi (lazy import joyida bo'linadi)

Code splitting — bitta katta bundle'ni kichik, mustaqil bo'laklarga (chunk) bo'lish texnikasi. Butun ilova bitta bundle.js (2 MB) o'rniga, mantiqiy bo'laklarga ajratiladi: main.js (asosiy, kichik — darrov yuklanadi), home.chunk.js (bosh sahifa), admin.chunk.js (faqat admin ochganda), editor.chunk.js (faqat muharrir kerak bo'lganda). On-demand yuklash: foydalanuvchi bosh sahifani ochganda faqat main.js + home.chunk.js (80 KB — tez) yuklanadi; admin sahifasiga o'tganda admin.chunk.js o'sha paytda yuklanadi (bir martalik kichik kechikish); grafik ko'rsatilganda chart.chunk.js yuklanadi. Ikki muhim nuqta: (1) har chunk — alohida fayl, brauzer uni keshlaydi (10.2 — content hash bilan), shuning uchun ikkinchi marta yuklanmaydi (tez); (2) Vite/Webpack chunk'larni avtomatik yaratadi — siz faqat "bu yerda bo'l" deb belgilaysiz (dinamik import() orqali — 2.3), bundler qolganini qiladi. Code splitting — kodni o'zgartirmasdan (faqat import usulini o'zgartirib) yuklanish tezligini keskin oshiradi.

2.3. Dinamik import() — poydevor

text
  STATIK import (odatdagi — fayl boshida, BUNDLE'ga kiradi — 2.14):
  import { chart } from "./chart.js";   // har doim yuklanadi (asosiy bundle'da)

  DINAMIK import() (funksiya kabi — ALOHIDA chunk, KERAK bo'lganda):
  const module = await import("./chart.js");   // Promise qaytaradi! (asinxron)
  module.chart();                               // yuklangandan keyin ishlatamiz

  ┌────────────────────────────────────────────────────────────┐
  │ import X from "..."   statik (kompilyatsiyada hal, bundle'da)│
  │ import("...")         dinamik (ish vaqtida, alohida chunk)   │
  └────────────────────────────────────────────────────────────┘

  DINAMIK import:
  - Promise qaytaradi (modul yuklangach hal bo'ladi)
  - Bundler bu joyda bundle'ni BO'LADI (alohida chunk yaratadi — avtomatik)
  - Faqat chaqirilganda yuklaydi (on-demand)

   Dinamik import() — code splitting'ning POYDEVORI (React.lazy uning ustida)
   Og'ir kutubxonani ham shunday: const { jsPDF } = await import("jspdf");  // faqat kerak bo'lganda

Dinamik import() — code splitting'ning poydevori. Odatdagi statik import (import X from "..." — fayl boshida) modulni har doim asosiy bundlega qo'shadi (11.2: 2.14). Dinamik import esa funksiya kabi ishlatiladi — import("./chart.js") — va u Promise qaytaradi (asinxron — modul yuklangach hal bo'ladi): const module = await import("./chart.js"). Eng muhimi — bundler (Vite/Webpack) aynan shu joyda bundle'ni bo'ladi, ya'ni chart.jsni alohida chunk qilib ajratadi va uni faqat import() chaqirilganda yuklaydi (on-demand). Bu — JavaScript standarti (React'ga xos emas), shuning uchun og'ir kutubxonalarni ham shunday lazy yuklash mumkin: const { jsPDF } = await import("jspdf") — PDF kutubxonasi faqat foydalanuvchi "PDF yuklab ol" bosganda yuklanadi (asosiy bundle'ni kichraytiradi). React.lazy 2.4-bob — aynan shu dinamik import() ustiga qurilgan: u komponentni dinamik import bilan yuklaydi va React'ning render tizimiga ulaydi. Demak dinamik import — pastki qatlam (mexanizm), React.lazy/Suspense — uning React'dagi qulay o'rovi.

2.4. React.lazy — komponentni dangasa yuklash

text
  React.lazy — komponentni DINAMIK import bilan "dangasa" (kerak bo'lganda) yuklaydi:

  import { lazy } from "react";

  // ESKI (statik — asosiy bundle'da):
  import Dashboard from "./Dashboard";

  // YANGI (lazy — alohida chunk, kerak bo'lganda):
  const Dashboard = lazy(() => import("./Dashboard"));
  //                       └─ dinamik import qaytaradigan funksiya 2.3-bob

  ISHLATISH — oddiy komponent kabi (LEKIN Suspense ichida — 2.5):
  <Suspense fallback={<Spinner />}>
    <Dashboard />               {/* yuklanganda render, yuklanayotganda fallback */}
  </Suspense>

  ┌────────────────────────────────────────────────────────────┐
  │ lazy(() => import("./X"))    X alohida chunk; faqat render  │
  │   bo'lganda yuklanadi (yuklanguncha Suspense fallback)        │
  └────────────────────────────────────────────────────────────┘

   React.lazy FAQAT default export bilan ishlaydi (named uchun — 2.11)
   lazy komponent ALBATTA Suspense ichida bo'lishi kerak (aks holda xato — 2.5)

React.lazy — komponentni dinamik import bilan "dangasa" yuklaydigan funksiya: const Dashboard = lazy(() => import("./Dashboard")). Argument — dinamik import qaytaradigan funksiya 2.3-bob. Bu shuni anglatadi: Dashboard komponenti asosiy bundle'ga kirmaydi, balki alohida chunk bo'lib ajraladi va faqat render bo'lganda (ekranda ko'rsatilishi kerak bo'lganda) yuklanadi. Ishlatish oddiy komponent kabi (<Dashboard />), lekin u albatta <Suspense> ichida bo'lishi shart 2.5-bob — chunki yuklanish vaqt oladi (asinxron), va o'sha paytda React nima ko'rsatishni bilishi kerak (fallback). Ikki muhim qoida: (1) React.lazy faqat default export bilan ishlaydi (named export uchun maxsus naqsh — 2.11); (2) lazy komponent albatta Suspense ichida bo'lishi kerak — aks holda React xato beradi ("suspended while responding to synchronous input"). React.lazy — komponent darajasidagi code splitting'ning standart vositasi: bitta qator o'zgartirib (import lazy(() => import)), komponentni alohida chunk qilasiz.

2.5. Suspense — fallback UI (yuklanish paytida)

text
  Suspense — ichidagi lazy komponent yuklanayotgan paytda ZAXIRA UI ko'rsatadi:

  <Suspense fallback={<Spinner />}>    {/* fallback — yuklanayotganda ko'rinadi */}
    <Dashboard />                       {/* tayyor bo'lganda — bu ko'rinadi */}
  </Suspense>

  JARAYON:
  1. <Dashboard /> render bo'lmoqchi  kodi hali yo'q (yuklanmoqda)
  2. React "to'xtaydi" (suspend)  fallback (Spinner) ni ko'rsatadi
  3. Dashboard.chunk.js yuklandi  React Dashboard'ni ko'rsatadi (Spinner o'rniga)

  ┌──────────┐  yuklanmoqda  ┌──────────┐  yuklandi  ┌─────────────┐
  │ <Spinner>│ ─────────────►│  (kutish) │ ──────────►│ <Dashboard> │
  │(fallback)│               └──────────┘            └─────────────┘
  └──────────┘

  BITTA Suspense — bir nechta lazy komponentni o'rashi mumkin (hammasi tayyor bo'lguncha fallback):
  <Suspense fallback={<Spinner />}>
    <Header /> <Sidebar /> <Content />   {/* uchchovi lazy bo'lsa — hammasi kutiladi */}
  </Suspense>

   fallback — yuklanish paytidagi UI (spinner, skeleton — 2.9)
   Suspense joylashuvi muhim: qayerda qo'ysangiz — o'sha "chegara" kutish ko'rsatadi (2.6)

Suspense — ichidagi lazy komponent(lar) yuklanayotgan paytda zaxira UI (fallback) ko'rsatadigan komponent: <Suspense fallback={<Spinner />}>. Jarayon: (1) lazy <Dashboard /> render bo'lmoqchi, lekin kodi hali yuklanmagan; (2) React "to'xtaydi" (suspend) va fallbackni (Spinner) ko'rsatadi; (3) Dashboard.chunk.js yuklangach, React Spinner o'rniga Dashboardni ko'rsatadi — barchasi avtomatik. Bitta Suspense bir nechta lazy komponentni o'rashi mumkin — bunda hammasi tayyor bo'lguncha fallback ko'rinadi (yoki har birini alohida Suspense bilan o'rab, mustaqil yuklash — 2.6). Ikki muhim nuqta: (1) fallback — yuklanish paytidagi UI; oddiy spinner emas, skeleton (kontent shaklidagi "ko'lanka") foydalanuvchi tajribasini yaxshilaydi 2.9-bob; (2) Suspensening joylashuvi muhim — uni qayerda qo'ysangiz, o'sha "chegara" kutish holatini ko'rsatadi (masalan butun sahifani yoki faqat bir bo'lakni — 2.6). Suspense — React'ning yuklanish (va keyinchalik data fetching — 2.12) holatini deklarativ boshqarish vositasi.

2.6. Route-based code splitting (eng keng tarqalgan)

text
  ROUTE-BASED SPLITTING — har SAHIFANI alohida chunk qilish (eng tabiiy bo'linish):

  // Har sahifa lazy (foydalanuvchi o'sha sahifaga o'tganda yuklanadi):
  const Home = lazy(() => import("./pages/Home"));
  const Dashboard = lazy(() => import("./pages/Dashboard"));
  const Settings = lazy(() => import("./pages/Settings"));

  function App() {
    return (
      <Suspense fallback={<PageLoader />}>     {/* sahifa yuklanayotganda */}
        <Routes>                                {/* React Router — 11.9 */}
          <Route path="/" element={<Home />} />
          <Route path="/dashboard" element={<Dashboard />} />
          <Route path="/settings" element={<Settings />} />
        </Routes>
      </Suspense>
    );
  }

  NATIJA:
  - Bosh sahifa  faqat Home chunk (admin/settings YUKLANMAYDI)
  - /dashboard  o'sha paytda Dashboard chunk yuklanadi
   har sahifa mustaqil yuklanadi (eng katta tejamkorlik)

   Eng keng amaliyot: har route — lazy (sahifalar tabiiy ravishda mustaqil)
   Suspense odatda Routes'ni o'raydi (sahifa almashishida fallback)

Route-based code splitting — eng keng tarqalgan va eng tabiiy bo'linish: har sahifani (route) alohida chunk qilish. Har sahifa komponentini lazy(() => import("./pages/X")) bilan e'lon qilasiz va <Suspense> bilan <Routes>ni o'raysiz (React Router — 11.9). Natija: foydalanuvchi bosh sahifani ochganda faqat Home chunk yuklanadi (admin, settings, dashboard — yuklanmaydi); /dashboardga o'tganda o'sha paytda Dashboard chunk yuklanadi (bir martalik kichik kechikish, keyin keshlanadi). Bu — eng katta tejamkorlik beradi, chunki sahifalar tabiiy ravishda mustaqil (foydalanuvchi bir vaqtda bitta sahifani ko'radi). Bu eng keng amaliyot: real ilovada deyarli har route lazy bo'ladi. Suspense odatda Routesni o'raydi (sahifa almashishida fallback ko'rsatish uchun); ko'proq nazorat kerak bo'lsa, har route'ni alohida Suspense bilan o'rash mumkin (har sahifaning o'z loading holati). Next.js'da bu avtomatik (har sahifa o'z-o'zidan code-split bo'ladi — 13.10), Vite/React Router'da esa qo'lda lazy qo'shasiz.

2.7. Component-based splitting (og'ir komponentlar)

text
  COMPONENT-BASED SPLITTING — og'ir/kam ishlatiladigan KOMPONENTNI alohida chunk:

  QAYSI komponentlarni bo'lish (asosiy bundle'dan chiqarish):
  - Og'ir kutubxonaga bog'liq (grafik — Chart.js, muharrir — TinyMCE, xarita — Leaflet)
  - Kam ochiladigan (modal, sozlamalar oynasi, video pleer)
  - Shartli (faqat admin/login bo'lsa ko'rinadi)

  const HeavyChart = lazy(() => import("./HeavyChart"));   // 300 KB grafik

  function Dashboard() {
    const [showChart, setShowChart] = useState(false);
    return (
      <div>
        <button onClick={() => setShowChart(true)}>Grafikni ko'rsat</button>
        {showChart && (                          // shartli — faqat bosilganda yuklanadi
          <Suspense fallback={<div>Grafik yuklanmoqda...</div>}>
            <HeavyChart />
          </Suspense>
        )}
      </div>
    );
  }

   Grafik kodi (300 KB) faqat foydalanuvchi "ko'rsat" bossa yuklanadi (bosh yuk kichik)
   Modal/og'ir widget — klassik component-based splitting nomzodi

Component-based splitting — route emas, alohida og'ir komponentni chunk qilish. Bu route-based'ni to'ldiradi: ba'zi komponentlar bitta sahifa ichida bo'lsa ham, alohida bo'lishga arziydi. Qaysi komponentlarni bo'lish: (1) og'ir kutubxonaga bog'liq (grafik — Chart.js/Recharts, matn muharriri — TinyMCE, xarita — Leaflet/Mapbox — 8.28); (2) kam ochiladigan (modal, sozlamalar oynasi, video pleer, emoji tanlagich); (3) shartli ko'rinadigan (faqat admin yoki login bo'lganda). Misol: const HeavyChart = lazy(() => import("./HeavyChart")) — 300 KB grafik komponenti; uni {showChart && <Suspense><HeavyChart /></Suspense>} bilan shartli render qilsangiz, grafik kodi faqat foydalanuvchi "Grafikni ko'rsat" bosganda yuklanadi. Foyda: og'ir kutubxona (300 KB) asosiy bundle'dan chiqadi bosh yuklanish kichik va tez; grafik faqat haqiqatan kerak bo'lganda (kichik qism foydalanuvchi) yuklanadi. Modal va og'ir widgetlar — component-based splitting'ning klassik nomzodlari (ko'pchilik foydalanuvchi ularni ochmaydi).

2.8. Error boundary bilan — yuklanish xatosini ushlash

text
  MUAMMO: lazy chunk yuklanishi XATO bo'lsa (tarmoq uzildi, fayl yo'q)?
   Suspense FAQAT yuklanishni kuzatadi; XATONI ushlamaydi  ilova buziladi (oq ekran)

  YECHIM — ERROR BOUNDARY (xato chegarasi) bilan o'rash:
  <ErrorBoundary fallback={<p>Yuklab bo'lmadi. Qayta urinib ko'ring.</p>}>
    <Suspense fallback={<Spinner />}>
      <LazyComponent />
    </Suspense>
  </ErrorBoundary>

  3 QATLAM:
  - ErrorBoundary  yuklanish/render XATOSINI ushlaydi (zaxira UI)
  - Suspense       yuklanish KUTISHINI boshqaradi (fallback)
  - LazyComponent  asl kontent

  ┌────────────────────────────────────────────────────────────┐
  │ ErrorBoundary (xato) > Suspense (kutish) > Lazy (kontent)     │
  │  tarmoq uzilsa: oq ekran emas, "qayta urinish" tugmasi      │
  └────────────────────────────────────────────────────────────┘

   Error boundary — 11.12'da chuqur; lazy bilan BIRGA ishlatish — production majburiy
   React 19: chunk yuklanish xatosida AVTOMATIK qayta urinish (yaxshilangan)

Error boundary bilan — lazy yuklash uchun production'da majburiy himoya. Muammo: lazy chunk yuklanishi xato bo'lishi mumkin (foydalanuvchi tarmog'i uzildi, server fayl bermadi, eski cache) — Suspense faqat yuklanishni (kutishni) kuzatadi, xatoni ushlamaydi, shuning uchun xato bo'lsa ilova buziladi (oq ekran). Yechim — lazy/Suspense'ni Error Boundary (xato chegarasi) bilan o'rash: <ErrorBoundary fallback={<p>Yuklab bo'lmadi...</p>}> ichida <Suspense>. Uch qatlam: ErrorBoundary (yuklanish/render xatosini ushlaydi zaxira UI), Suspense (yuklanish kutishini boshqaradi fallback), LazyComponent (asl kontent). Natija — tarmoq uzilsa foydalanuvchi oq ekran emas, "qayta urinib ko'ring" tugmasini ko'radi. Error boundary 11.12-bobda chuqur o'rganiladi (hozircha bil: u render xatolarini ushlaydigan komponent); lazy bilan birga ishlatish — production amaliyoti. React 19 yangiligi: lazy chunk yuklanishi xato bo'lsa, React avtomatik qayta urinadi (vaqtincha tarmoq muammosi o'tib ketsa tiklanadi) — bu lazy yuklashni ancha ishonchli qiladi.

2.9. Loading holatlari — spinner vs skeleton

text
  FALLBACK UI — yuklanish paytidagi ko'rinish (foydalanuvchi tajribasiga ta'sir qiladi):

  1. SPINNER (aylanuvchi belgi) — oddiy, lekin "bo'sh kutish":
  <Suspense fallback={<Spinner />}>

  2. SKELETON (kontent shakli — "ko'lanka") — yaxshiroq (kutilgan shaklni ko'rsatadi):
  <Suspense fallback={<CardSkeleton />}>   {/* karta shaklidagi kulrang bloklar */}
   foydalanuvchi "nima keladi"ni ko'radi, kutish qisqaroq tuyuladi (idrok)

  3. AVVALGI KONTENTNI ushlab turish (React 18+ — useTransition — sahifa almashishda):
   eski sahifa ko'rinib turadi, yangisi fonda yuklanadi (sakrash yo'q)

  ┌────────────────────────────────────────────────────────────┐
  │ Spinner: oddiy holat | Skeleton: ro'yxat/karta (afzal) |    │
  │ Transition: sahifa almashishi (sakrashsiz — 2.12)          │
  └────────────────────────────────────────────────────────────┘

   Skeleton > spinner (idrok etilgan tezlik — "layout shift" kamroq — 13.8)
   Juda qisqa yuklanishda fallback "miltillamasin" (delay yoki minimal vaqt)

Loading holatlarifallback UI tanlovi foydalanuvchi tajribasiga sezilarli ta'sir qiladi. Uch yondashuv: (1) Spinner (aylanuvchi belgi) — oddiy va keng, lekin "bo'sh kutish" hissini beradi (foydalanuvchi nima kelishini bilmaydi). (2) Skeleton (kontent shaklidagi kulrang "ko'lanka" bloklar) — afzalroq: foydalanuvchi "nima keladi"ni (karta, ro'yxat, matn) oldindan ko'radi, shuning uchun kutish qisqaroq tuyuladi (idrok etilgan tezlik), va kontent kelganda layout shift (sakrash) kamroq bo'ladi (13.8 — CLS). (3) Transition (React 18+ useTransition) — sahifa almashganda eski kontentni ushlab turadi, yangisi fonda yuklanadi (sakrashsiz — 2.12). Ikki amaliy maslahat: (1) skeleton spinner'dan yaxshiroq (ayniqsa ro'yxat/karta sahifalarda — idrok etilgan tezlik va kam layout shift); (2) juda qisqa yuklanishda fallback miltillab ketmasin (bir zumda ko'rinib yo'qolsa — yomon) — buni minimal ko'rsatish vaqti yoki kichik kechikish (delay) bilan hal qilinadi. To'g'ri fallback — code splitting'ning foydasini maksimal qiladi.

2.9-a. useTransition/startTransition — sakrashsiz almashish

text
  MUAMMO: lazy sahifaga o'tganda Suspense fallback butun ekranni "yeb" qo'yadi:
   eski sahifa DARROV yo'qoladi  oq/skeleton ekran  yangi sahifa (sakrash, miltillash)

  YECHIM — startTransition/useTransition (React 18+): o'tishni "shoshilmas" deb belgila:
   React eski UI'ni EKRANDA ushlab turadi, yangi chunk fonda yuklanadi,
    tayyor bo'lganda almashadi (Suspense fallback KO'RINMAYDI — sakrash yo'q)

  const [isPending, startTransition] = useTransition();

  function goTo(path) {
    startTransition(() => {       //  bu ichidagi state yangilanishi "transition"
      navigate(path);             //     eski sahifa ko'rinib turadi (isPending=true)
    });
  }
  // isPending — o'tish davomida true (masalan yuqorida ingichka progress chizig'i)

  ┌────────────────────────────────────────────────────────────┐
  │ Transition'siz: eski UI yo'qoladi  fallback  yangi (sakrash)│
  │ Transition bilan: eski UI qoladi  tayyor bo'lgach almashadi │
  └────────────────────────────────────────────────────────────┘

   startTransition — bir martalik; useTransition — isPending (progress ko'rsatkichi) beradi
   Suspense bilan birga: transition ichidagi suspend fallback'ni KO'RSATMAYDI (eski UI qoladi)

useTransition va startTransition — lazy sahifa almashishidagi "sakrash" muammosini hal qiladi (React 18+). Muammo: Suspense bilan lazy route'ga o'tganda, eski sahifa darrov yo'qoladi va yangi chunk yuklanguncha butun ekran fallback (skeleton/spinner) bilan almashadi — bu miltillash va layout sakrashiga olib keladi. Yechim: navigatsiyani startTransition ichiga o'raysiz — bu React'ga "bu yangilanish shoshilmas" deb aytadi. Natijada React eski UI'ni ekranda ushlab turadi va yangi chunk fonda yuklanadi; tayyor bo'lganda esa silliq almashadi — transition ichidagi suspend Suspense fallback'ini ko'rsatmaydi (sakrash yo'q). useTransition hook'i qo'shimcha isPending (boolean) qaytaradi — o'tish davomida true, shuning uchun sahifaning yuqorisida ingichka progress chizig'i yoki xira effekt ko'rsatib, foydalanuvchiga "o'tmoqda" degan signal berish mumkin. Ikki nuqta: (1) startTransition(fn) — bir martalik funksiya (masalan tugma ichida), useTransition esa isPending bilan progress holatini ham beradi; (2) bu ayniqsa route-based splitting 2.6-bob bilan kuchli — sahifadan sahifaga o'tish "app-ga o'xshab" silliq bo'ladi (to'liq oq ekransiz). React Router 11.9-bob navigatsiyani ichki tarzda transition sifatida boshqaradi, shuning uchun ko'p holatda buni router o'zi qiladi.

2.10. Preload va prefetch — oldindan yuklash

text
  MUAMMO: lazy yuklash "kech" — foydalanuvchi bosgandan KEYIN yuklash boshlanadi (kechikish)
  YECHIM: kerak bo'lishini OLDIN bilsang — fonda oldindan yukla (preload/prefetch)

  HOVER'DA PRELOAD (foydalanuvchi tugmaga yaqinlashdi  yuklashni boshla):
  const Settings = lazy(() => import("./Settings"));
  const preloadSettings = () => import("./Settings");   // import'ni oldindan chaqir

  <Link to="/settings" onMouseEnter={preloadSettings}>Sozlamalar</Link>
  // foydalanuvchi hover qildi  chunk fonda yuklanmoqda  bosganda DARROV tayyor

  PREFETCH (bo'sh vaqtda kelajakda kerak bo'ladigan chunk'larni fonda):
  - foydalanuvchi bosh sahifada  keyingi ehtimoliy sahifani prefetch
  - <link rel="prefetch"> yoki router avtomatik (Next.js — 13.10)

  ┌────────────────────────────────────────────────────────────┐
  │ Lazy: kerak bo'lganda (bosilgach) | Preload: hover'da (oldin) │
  │  preload kechikishni YO'QOTADI (chunk allaqachon yuklangan)  │
  └────────────────────────────────────────────────────────────┘

   import("./X") faqat BIR marta yuklaydi (keshlangan — qayta chaqirish tez)
   Preload — eng yaxshi UX: lazy tejamkorligi + tez ochilish (kechikishsiz)

Preload va prefetch — lazy yuklashning kichik kamchiligini (foydalanuvchi bosgandan keyin yuklash boshlanadi kechikish) hal qiladi. G'oya: agar komponent kerak bo'lishini oldindan bilsangiz (yoki taxmin qilsangiz), uni fonda oldindan yukla. Preload (hover'da): foydalanuvchi tugmaga/havolaga sichqonchani olib borganda (onMouseEnter), import("./Settings")ni chaqirib chunk'ni fonda yuklashni boshlaysiz — foydalanuvchi bosganda chunk allaqachon tayyor (kechikishsiz). Bu — eng yaxshi tajriba: lazy'ning tejamkorligi + tez ochilish. Prefetch: bo'sh vaqtda (sahifa yuklangach) kelajakda ehtimoliy kerak bo'ladigan chunk'larni fonda yuklash (<link rel="prefetch"> yoki router avtomatik — Next.js buni o'zi qiladi — 13.10). Ikki nuqta: (1) import("./X") modulni faqat bir marta yuklaydi (keshlangan — keyingi chaqirish tez, qayta yuklamaydi), shuning uchun preload xavfsiz; (2) preload eng yaxshi UX'ni beradi — tejamkorlik (kerak bo'lmasa yuklanmaydi) va tezlik (hover'da oldindan). React Router 11.9-bob va Next.js bu naqshlarni qo'llab-quvvatlaydi.

2.11. Named export bilan lazy

text
  MUAMMO: React.lazy FAQAT default export bilan ishlaydi 2.4-bob:
  //  Agar komponent named export bo'lsa:
  // export function Dashboard() {...}   (default emas)
  // const Dashboard = lazy(() => import("./Dashboard"));   // ishlamaydi (default kutadi)

  YECHIM — import natijasini default'ga "moslash":
  const Dashboard = lazy(() =>
    import("./components").then(module => ({ default: module.Dashboard }))
    //                                      └─ named export'ni default qilib qaytar
  );

  YOKI — oraliq fayl (re-export default):
  // Dashboard.lazy.js:  export { Dashboard as default } from "./components";
  const Dashboard = lazy(() => import("./Dashboard.lazy"));

   React.lazy import natijasida `.default` ni kutadi (shuning uchun moslash kerak)
   Eng oson: lazy yuklanadigan komponentni DEFAULT export qil (muammo umuman bo'lmaydi)

Named export bilan lazyReact.lazyning bir cheklovini hal qiladi: u faqat default export bilan ishlaydi (import() natijasida .defaultni kutadi). Agar komponent named export bo'lsa (export function Dashboard), to'g'ridan lazy(() => import("./Dashboard")) ishlamaydi. Yechim — import natijasini .defaultga moslash: lazy(() => import("./components").then(module => ({ default: module.Dashboard }))) — ya'ni named export'ni { default: ... } obyektiga o'rab qaytarasiz. Yoki oraliq fayl orqali re-export: export { Dashboard as default } from "./components", keyin uni lazy qilasiz. Ikki maslahat: (1) bu naqshni bil (mavjud named export'li komponentni lazy qilish kerak bo'lganda); (2) lekin eng oson yo'l — lazy yuklanadigan komponentlarni default export qilish (shunda muammo umuman bo'lmaydi). Bu — kichik texnik tafsilot, lekin amaliyotda tez-tez uchraydi (ko'p loyiha named export ishlatadi).

2.12. Suspense data fetching uchun (React 18+/19 — use())

text
  Suspense FAQAT lazy komponent uchun emas — DATA FETCHING uchun ham (React 18+/19):

  ESKI yondashuv (har komponentda loading/error — takror — 11.5):
  const { data, loading, error } = useFetch(url);
  if (loading) return <Spinner/>; if (error) return <Error/>; return <div>{data}</div>;

  YANGI (Suspense bilan — loading/error komponentdan CHIQADI):
  function Profile() {
    const user = use(fetchUser());      // React 19 — use() Promise'ni "kutadi" (suspend)
    return <div>{user.name}</div>;      // faqat TAYYOR holat (loading/error bu yerda YO'Q)
  }
  // O'rab:
  <ErrorBoundary fallback={<Error/>}>   {/* xato bu yerda */}
    <Suspense fallback={<Spinner/>}>    {/* loading bu yerda */}
      <Profile />                        {/* faqat data — toza */}
    </Suspense>
  </ErrorBoundary>

  ┌────────────────────────────────────────────────────────────┐
  │ Loading  Suspense; Error  ErrorBoundary; Komponent  toza  │
  │ (deklarativ: holatlar UI daraxtida, komponentda emas)        │
  └────────────────────────────────────────────────────────────┘

   use() — React 19 hook'i (Promise/Context o'qiydi; shartli ham chaqirsa bo'ladi)
   Amalda — TanStack Query/Next.js Suspense'ni soddalashtirib beradi (12.4, 13.5)

Suspense data fetching uchunSuspense faqat lazy komponent uchun emas, ma'lumot olish (data fetching) uchun ham ishlatiladi (React 18+/19). Eski yondashuvda har komponent o'zi loading/error/data holatlarini boshqaradi (takror — 11.5). Yangi yondashuvda bu holatlar komponentdan chiqariladi: komponent faqat tayyor holatni qaytaradi, loadingSuspensega, errorErrorBoundaryga o'tadi. React 19'ning use() hook'i Promise'ni "kutadi" (suspend qiladi): const user = use(fetchUser()) — agar ma'lumot tayyor bo'lmasa, komponent suspend bo'ladi va eng yaqin Suspense fallback'ni ko'rsatadi; tayyor bo'lganda komponent render bo'ladi. Natija — komponent toza (faqat data mantig'i), holatlar UI daraxtida deklarativ joylashgan: loading Suspense, error ErrorBoundary. Ikki nuqta: (1) use() — React 19 hook'i (Promise yoki Context o'qiydi, va g'ayrioddiy — shart ichida ham chaqirsa bo'ladi); (2) amaliyotda bu naqshni qo'lda yozish murakkab (Promise cache kerak) — shuning uchun TanStack Query 12.4-bob yoki Next.js Server Components 13.5-bob Suspense data fetching'ni soddalashtirib beradi. Bu — React'ning kelajagi (concurrent yondashuv).

2.13. Nimani bo'lish va o'lchov (bundle analiz)

text
  CODE SPLITTING'ni O'LCHOVGA asoslab qil (taxminga emas — 11.6: 2.13):

  1. BUNDLE'ni TAHLIL qil (nima og'irligini ko'r):
  - vite-bundle-visualizer / rollup-plugin-visualizer (Vite)
  - "qaysi modul/kutubxona eng katta?" — vizual xarita

  2. NIMANI bo'lish (kattalik va kerak'lik bo'yicha):
   Route'lar (har sahifa — tabiiy bo'linish — 2.6)
   Og'ir kutubxonaga bog'liq komponentlar (grafik, muharrir, xarita — 2.7)
   Kam ochiladigan (modal, admin panel)

  NIMANI BO'LMASLIK:
   Kichik komponentlar (chunk yaratish — alohida tarmoq so'rovi; kichigida foydadan ko'p xarajat)
   Har doim ko'rinadigan (header, navbar — baribir darrov kerak)

  ┌────────────────────────────────────────────────────────────┐
  │ Avval o'lcha (visualizer)  eng katta/kam ishlatiladiganni  │
  │ bo'l  qayta o'lcha (foyda bormi?) — dalilga asoslan         │
  └────────────────────────────────────────────────────────────┘

   Juda mayda bo'lish — ko'p kichik so'rov (HTTP overhead)  teskari effekt
   Balans: katta chunklarni bo'l, mayda komponentlarni qoldir (11.6: 2.13 falsafasi)

Nimani bo'lish va o'lchov — code splitting ham o'lchovga asoslangan bo'lishi kerak (11.6: 2.13 falsafasi — taxminga emas, dalilga). Jarayon: (1) avval bundle'ni tahlil qil — rollup-plugin-visualizer (Vite) yoki shunga o'xshash vosita bilan "qaysi modul/kutubxona eng katta?" degan vizual xaritani ko'r; (2) nimani bo'lish — kattalik va kerak'lik bo'yicha tanla: route'lar (har sahifa — tabiiy — 2.6), og'ir kutubxonaga bog'liq komponentlar (grafik, muharrir, xarita — 2.7), kam ochiladigan (modal, admin). Nimani bo'lmaslik: kichik komponentlar (har chunk — alohida tarmoq so'rovi; kichik komponentda bu foydadan ko'p xarajat); har doim ko'rinadigan narsalar (header, navbar — baribir darrov kerak, bo'lishning ma'nosi yo'q). Asosiy balans: juda mayda bo'lish teskari ta'sir qiladi — ko'p kichik chunk = ko'p HTTP so'rov (overhead, ayniqsa HTTP/1.1'da). To'g'ri strategiya: katta chunklarni (og'ir kutubxonalar, sahifalar) bo'l, mayda komponentlarni asosiy bundle'da qoldir. Avval o'lcha bo'l qayta o'lcha (foyda bordimi?) — bu dalilga asoslangan optimizatsiya.

Vendor split (manualChunks). React.lazy — kod bo'linishining bir usuli, lekin bundler'ning o'zi ham chunk'larni sozlashga imkon beradi. Vite/Rollup'da build.rollupOptions.output.manualChunks orqali kutubxonalarni (node_modules) alohida vendor chunk'ga ajratish mumkin (masalan react, react-domni bitta vendor chunk'ga). Buning foydasi — keshlash: sizning ilova kodingiz tez-tez o'zgaradi (har deploy'da yangi hash — 11.3: 2.6), lekin kutubxonalar kamdan-kam o'zgaradi; ularni alohida chunk qilsangiz, deploy'dan keyin foydalanuvchi faqat o'zgargan ilova kodini qayta yuklaydi, kutubxona chunk esa brauzer keshidan keladi (tez). Zamonaviy Vite ko'p holatda buni oqilona avtomatik qiladi, shuning uchun manualChunksni faqat visualizer aniq bir muammoni ko'rsatganda qo'lda sozlang (erta optimizatsiya qilmang).

2.14. SSR va Next.js konteksti (qisqa)

text
  CLIENT-SIDE (Vite/React) — bu bobda o'rgangan: React.lazy + Suspense (qo'lda)

  SSR / NEXT.JS (13-QISM) — code splitting ko'p jihatda AVTOMATIK:
  - Har sahifa (route) AVTOMATIK alohida chunk (qo'lda lazy kerak emas — 13.2)
  - next/dynamic — komponent darajasida lazy (React.lazy'ning Next versiyasi):
    const Chart = dynamic(() => import("./Chart"), { loading: () => <Spinner/>, ssr: false });
  - Server Components 13.3-bob — kod serverda qoladi, mijozga umuman yuborilmaydi (eng katta tejamkorlik)
  - Avtomatik prefetch (<Link> ko'rinsa keyingi sahifani fonda — 2.10)

  ┌────────────────────────────────────────────────────────────┐
  │ Vite/React: lazy + Suspense QO'LDA (bu bob)                 │
  │ Next.js: ko'pi AVTOMATIK + next/dynamic + Server Components │
  └────────────────────────────────────────────────────────────┘

   React.lazy SSR'da to'g'ridan ishlamaydi  Next.js'da next/dynamic ishlat (13.10)
   Tushuncha BIR XIL (chunk, on-demand); faqat Next.js ko'rini avtomatlashtiradi

SSR va Next.js konteksti — code splitting Vite/React'da (bu bobda) qo'lda (React.lazy + Suspense), Next.js'da esa (13-QISM) ko'p jihatda avtomatik. Next.js'da: har sahifa (route) avtomatik alohida chunk bo'ladi (qo'lda lazy qo'shish kerak emas — 13.2); komponent darajasida lazy uchun next/dynamic ishlatiladi (React.lazyning Next.js versiyasi, ssr: false opsiyasi bilan — SSR'ni o'chirib, faqat mijozda yuklash mumkin); Server Components 13.3-bob esa eng kuchli — komponent kodi serverda qoladi va mijozga umuman yuborilmaydi (eng katta tejamkorlik); va <Link> avtomatik prefetch qiladi (keyingi sahifani fonda — 2.10). Ikki muhim nuqta: (1) React.lazy SSR muhitida (server'da render) to'g'ridan ishlamaydi — Next.js'da next/dynamic ishlat 13.10-bob; (2) lekin tushuncha bir xil — chunk, on-demand yuklash, fallback; faqat Next.js ko'p ishni avtomatlashtiradi. Demak bu bobda o'rgangan g'oyalar (lazy, Suspense, splitting strategiyasi) Next.js'da ham amal qiladi, faqat sintaksis va avtomatlik darajasida farq bor.


3. Sintaksis — tez ma'lumotnoma

text
DINAMIK IMPORT 2.3-bob:  const m = await import("./X")   // Promise; alohida chunk
React.lazy 2.4-bob:      const X = lazy(() => import("./X"))   // default export
Suspense 2.5-bob:        <Suspense fallback={<Spinner/>}><X/></Suspense>
ROUTE 2.6-bob:           const Page = lazy(() => import("./pages/Page"))  + Suspense > Routes
SHARTLI 2.7-bob:         {show && <Suspense fallback={...}><Heavy/></Suspense>}
ERROR 2.8-bob:           <ErrorBoundary><Suspense><X/></Suspense></ErrorBoundary>
NAMED 2.11-bob:          lazy(() => import("./X").then(m => ({ default: m.Named })))
TRANSITION (2.9-a):    const [isPending, startTransition] = useTransition()  // sakrashsiz
PRELOAD 2.10-bob:        onMouseEnter={() => import("./X")}   // hover'da oldindan
use() 2.12-bob:          const data = use(promise)   // React 19 — Suspense bilan
NEXT 2.14-bob:           dynamic(() => import("./X"), { loading, ssr: false })  // 13.10

4. Batafsil kod namunalari

Misol 1 — Dinamik import (poydevor — 2.3)

jsx
// Og'ir kutubxonani faqat kerak bo'lganda yuklash (PDF generatsiya)
function DownloadButton({ data }) {
  async function handleDownload() {
    const { jsPDF } = await import("jspdf");   //  faqat shu yerda (bosilganda) yuklanadi
    const doc = new jsPDF();
    doc.text(JSON.stringify(data), 10, 10);
    doc.save("hisobot.pdf");
  }
  return <button onClick={handleDownload}>PDF yuklab olish</button>;
}
//  jspdf (katta kutubxona) asosiy bundle'ga KIRMAYDI — faqat tugma bosilganda (on-demand)

Misol 2 — React.lazy + Suspense (asosiy — 2.4, 2.5)

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

const Dashboard = lazy(() => import("./Dashboard"));   // alohida chunk

function App() {
  const [showDashboard, setShowDashboard] = useState(false);
  return (
    <div>
      <button onClick={() => setShowDashboard(true)}>Dashboard ochish</button>
      {showDashboard && (
        <Suspense fallback={<p>Yuklanmoqda...</p>}>   {/* yuklanayotganda */}
          <Dashboard />                                 {/* tayyor bo'lganda */}
        </Suspense>
      )}
    </div>
  );
}
//  Dashboard kodi faqat "ochish" bosilganda yuklanadi (Suspense fallback ko'rsatadi)

Misol 3 — Route-based splitting (React Router — 2.6)

jsx
import { lazy, Suspense } from "react";
import { Routes, Route } from "react-router-dom";   // 11.9

// Har sahifa — alohida chunk (lazy):
const Home = lazy(() => import("./pages/Home"));
const Dashboard = lazy(() => import("./pages/Dashboard"));
const Settings = lazy(() => import("./pages/Settings"));
const Profile = lazy(() => import("./pages/Profile"));

function App() {
  return (
    <Suspense fallback={<PageLoader />}>     {/* sahifa almashishda */}
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/dashboard" element={<Dashboard />} />
        <Route path="/settings" element={<Settings />} />
        <Route path="/profile" element={<Profile />} />
      </Routes>
    </Suspense>
  );
}
//  Bosh sahifa  faqat Home chunk; /settings  o'sha paytda Settings chunk (eng keng amaliyot)

Misol 4 — Og'ir komponent splitting (grafik — 2.7)

jsx
const HeavyChart = lazy(() => import("./HeavyChart"));   // 300 KB (Recharts)

function Analytics() {
  const [tab, setTab] = useState("jadval");
  return (
    <div>
      <button onClick={() => setTab("jadval")}>Jadval</button>
      <button onClick={() => setTab("grafik")}>Grafik</button>

      {tab === "jadval" && <DataTable />}        {/* yengil — darrov */}
      {tab === "grafik" && (                       // grafik faqat shu tab ochilganda
        <Suspense fallback={<ChartSkeleton />}>
          <HeavyChart />
        </Suspense>
      )}
    </div>
  );
}
//  Foydalanuvchi "Grafik" tab'iga o'tmaca — 300 KB grafik kodi YUKLANMAYDI (bosh yuk kichik)

Misol 5 — Error boundary + Suspense (yuklanish xatosi — 2.8)

jsx
// Oddiy error boundary (11.12'da chuqur)
class ErrorBoundary extends React.Component {
  state = { hasError: false };
  static getDerivedStateFromError() { return { hasError: true }; }
  render() {
    if (this.state.hasError) {
      return (
        <div>
          <p>Komponentni yuklab bo'lmadi.</p>
          <button onClick={() => this.setState({ hasError: false })}>Qayta urinish</button>
        </div>
      );
    }
    return this.props.children;
  }
}

const RemoteWidget = lazy(() => import("./RemoteWidget"));

function App() {
  return (
    <ErrorBoundary>                          {/* xato  zaxira UI (oq ekran emas) */}
      <Suspense fallback={<Spinner />}>      {/* yuklanish  fallback */}
        <RemoteWidget />
      </Suspense>
    </ErrorBoundary>
  );
}
//  Tarmoq uzilca: oq ekran emas — "qayta urinish" tugmaci (production majburiy — 2.8)

Misol 6 — Skeleton fallback (yaxshi UX — 2.9)

jsx
// Skeleton — kontent shaklidagi "ko'lanka" (spinner'dan yaxshiroq)
function CardSkeleton() {
  return (
    <div className="card skeleton">
      <div className="skeleton-img" />          {/* kulrang bloklar (CSS animatsiya) */}
      <div className="skeleton-line" />
      <div className="skeleton-line short" />
    </div>
  );
}

const ProductCard = lazy(() => import("./ProductCard"));

function Catalog() {
  return (
    <Suspense fallback={
      <div className="grid">
        {Array.from({ length: 6 }).map((_, i) => <CardSkeleton key={i} />)}   {/* 6 ta skeleton */}
      </div>
    }>
      <ProductGrid />
    </Suspense>
  );
}
//  Skeleton "nima keladi"ni ko'rsatadi  idrok etilgan tezlik, kam layout shift (2.9, 13.8)

Misol 7 — Preload hover'da (kechikishni yo'qotish — 2.10)

jsx
const Settings = lazy(() => import("./pages/Settings"));

// import'ni alohida funksiya qilamiz (preload uchun)
const preloadSettings = () => import("./pages/Settings");

function Navbar() {
  return (
    <nav>
      <Link
        to="/settings"
        onMouseEnter={preloadSettings}        //  hover'da chunk fonda yuklanadi
        onFocus={preloadSettings}             // klaviatura uchun ham (a11y)
      >
        Sozlamalar
      </Link>
    </nav>
  );
}
//  Foydalanuvchi hover qildi  Settings chunk fonda  bosganda DARROV ochiladi (kechikishsiz)

Misol 8 — Named export bilan lazy (2.11)

jsx
// components.js da: export function Dashboard() {...}  (named, default emas)

//  const Dashboard = lazy(() => import("./components"));  // ishlamaydi

//  named export'ni default'ga moslash:
const Dashboard = lazy(() =>
  import("./components").then(module => ({ default: module.Dashboard }))
);

// Bir nechta named export'dan tanlash:
const Reports = lazy(() =>
  import("./components").then(m => ({ default: m.Reports }))
);
//  React.lazy `.default` kutadi  named'ni { default: ... } ga o'rab beramiz (2.11)

Misol 9 — Bir nechta lazy va ichma-ich Suspense (2.5, 2.6)

jsx
const Sidebar = lazy(() => import("./Sidebar"));
const Feed = lazy(() => import("./Feed"));
const Widgets = lazy(() => import("./Widgets"));

function Layout() {
  return (
    <div className="layout">
      {/* Tashqi Suspense — butun layout uchun */}
      <Suspense fallback={<PageSkeleton />}>
        <Sidebar />

        {/* Ichki Suspense — Feed mustaqil yuklanadi (Sidebar tayyor bo'lca ko'rinadi) */}
        <Suspense fallback={<FeedSkeleton />}>
          <Feed />
        </Suspense>

        <Suspense fallback={<WidgetsSkeleton />}>
          <Widgets />
        </Suspense>
      </Suspense>
    </div>
  );
}
//  Ichma-ich Suspense — har bo'lak MUSTAQIL yuklanadi (biri kutca, boshqalari ko'rinadi — 2.5)

Misol 10 — Modal'ni lazy (component-based — 2.7)

jsx
const SettingsModal = lazy(() => import("./SettingsModal"));   // og'ir modal — kam ochiladi

function Header() {
  const [showSettings, setShowSettings] = useState(false);
  return (
    <header>
      <button
        onClick={() => setShowSettings(true)}
        onMouseEnter={() => import("./SettingsModal")}   // preload hover'da 2.10-bob
      >
         Sozlamalar
      </button>
      {showSettings && (
        <Suspense fallback={<ModalSkeleton />}>
          <SettingsModal onClose={() => setShowSettings(false)} />
        </Suspense>
      )}
    </header>
  );
}
//  Modal kodi faqat ochilganda yuklanadi + hover'da preload  tez (ko'p foydalanuvchi ochmaydi)

Misol 11 — Suspense data fetching (React 19 use() — 2.12)

jsx
import { use, Suspense } from "react";

// fetchUser Promise qaytaradi (cache bilan — har render yangi Promise BO'LMASIN)
function Profile({ userPromise }) {
  const user = use(userPromise);            //  Promise'ni "kutadi" (tayyor bo'lmaca suspend)
  return <h2>{user.name}</h2>;              // faqat TAYYOR holat (loading/error YO'Q)
}

function App() {
  const userPromise = useMemo(() => fetchUser(1), []);   // barqaror Promise (11.6: 2.9)
  return (
    <ErrorBoundary fallback={<p>Xato</p>}>   {/* error bu yerda 2.8-bob */}
      <Suspense fallback={<Spinner />}>      {/* loading bu yerda */}
        <Profile userPromise={userPromise} />  {/* komponent toza — faqat data */}
      </Suspense>
    </ErrorBoundary>
  );
}
//  Loading  Suspense, Error  ErrorBoundary, Komponent  toza (deklarativ holatlar — 2.12)
//    Amalda TanStack Query/Next.js buni soddalashtiradi (12.4, 13.5)

Misol 12 — Minimal fallback vaqti (miltillamasin — 2.9)

jsx
// Juda tez yuklanca fallback "miltillab" ketmasligi uchun — kichik kechikish bilan ko'rsat
function DelayedFallback({ children, delay = 200 }) {
  const [show, setShow] = useState(false);
  useEffect(() => {
    const timer = setTimeout(() => setShow(true), delay);   // 200ms'dan keyin ko'rsat
    return () => clearTimeout(timer);
  }, [delay]);
  return show ? children : null;            // 200ms ichida yuklanca — fallback umuman ko'rinmaydi
}

function App() {
  return (
    <Suspense fallback={<DelayedFallback><Spinner /></DelayedFallback>}>
      <FastComponent />
    </Suspense>
  );
}
//  Tez chunk (kesh'dan) — spinner miltillamaydi; sekin — 200ms'dan keyin spinner (2.9)

Misol 13 — To'liq: lazy route + lazy og'ir komponent + preload + error

jsx
import { lazy, Suspense } from "react";
import { Routes, Route, Link } from "react-router-dom";

// Route-based (2.6)
const Home = lazy(() => import("./pages/Home"));
const Dashboard = lazy(() => import("./pages/Dashboard"));
const Reports = lazy(() => import("./pages/Reports"));

function App() {
  return (
    <ErrorBoundary>                                    {/* xato himoyasi 2.8-bob */}
      <nav>
        <Link to="/" onMouseEnter={() => import("./pages/Home")}>Bosh</Link>
        <Link to="/dashboard" onMouseEnter={() => import("./pages/Dashboard")}>Dashboard</Link>
        <Link to="/reports" onMouseEnter={() => import("./pages/Reports")}>Hisobotlar</Link>
      </nav>

      <Suspense fallback={<PageSkeleton />}>            {/* sahifa yuklanishda */}
        <Routes>
          <Route path="/" element={<Home />} />
          <Route path="/dashboard" element={<Dashboard />} />
          <Route path="/reports" element={<Reports />} />
        </Routes>
      </Suspense>
    </ErrorBoundary>
  );
}
//  Har sahifa alohida chunk + hover'da preload + xato himoyasi + skeleton — production naqsh

Misol 14 — useTransition bilan sakrashsiz navigatsiya (2.9-a)

jsx
import { useTransition } from "react";
import { useNavigate } from "react-router-dom";   // 11.9

function Nav() {
  const navigate = useNavigate();
  const [isPending, startTransition] = useTransition();

  function goTo(path) {
    startTransition(() => {     //  o'tishni "shoshilmas" deb belgila
      navigate(path);           //     eski sahifa ekranda qoladi, yangisi fonda yuklanadi
    });
  }

  return (
    <nav style={{ opacity: isPending ? 0.6 : 1 }}>   {/* o'tish davomida xira (signal) */}
      <button onClick={() => goTo("/dashboard")}>Dashboard</button>
      <button onClick={() => goTo("/reports")}>Hisobotlar</button>
      {isPending && <span> yuklanmoqda...</span>}
    </nav>
  );
}
//  Lazy sahifaga o'tganda Suspense fallback butun ekranni "yemaydi" — eski UI tayyor bo'lguncha qoladi (2.9-a)

5. To'g'ri va noto'g'ri holatlar

1) lazy + Suspense

text
 <LazyComp />  (Suspense'siz — xato: suspended while rendering)
 <Suspense fallback={...}><LazyComp /></Suspense>  (2.5)

2) Nimani bo'lish

text
 har kichik komponentni lazy (ko'p mayda chunk — HTTP overhead — 2.13)
 route + og'ir komponent/kutubxona (katta bo'laklar — 2.6, 2.7)

3) Yuklanish xatosi

text
 lazy faqat Suspense bilan (tarmoq uzilsa — oq ekran — 2.8)
 ErrorBoundary + Suspense + lazy (qayta urinish UI)

4) Suspense joyi

text
 butun ilovani bitta Suspense (har navigatsiyada to'liq oq ekran)
 maqsadli Suspense (route, og'ir komponent — mustaqil kutish — 2.5, 2.6)

5) Data fetching Promise

text
 use(fetchUser())  (har render YANGI Promise  cheksiz suspend — 2.12)
 use(memoizedPromise)  (barqaror Promise — useMemo/cache)

6) Fallback UX

text
 bo'sh yoki kech-keladigan fallback (sakrash, oq joy)
 skeleton (kontent shakli — kam layout shift — 2.9)

6. Keng tarqalgan xatolar va yechimlari

Xato 1 — A React component suspended while rendering, but no fallback UI was specified

Sababi: lazy komponent Suspense'siz render qilindi 2.5-bob. Yechimi: <Suspense fallback={...}> bilan o'ra.

Xato 2 — Lazy komponent oq ekran beradi (yuklanmaydi)

Sababi: named export'ni default sifatida kutdi 2.11-bob, yoki import yo'li xato. Yechimi: .then(m => ({ default: m.Named })) yoki default export; yo'lni tekshir.

Xato 3 — Failed to fetch dynamically imported module

Sababi: deploy'dan keyin eski chunk yo'qoldi (foydalanuvchi eski sahifada — yangi build'da fayl nomi o'zgardi — 11.3: 2.6 hash). Yechimi: Error boundary + qayta yuklash (window.location.reload()); React 19 avtomatik retry 2.8-bob.

Xato 4 — Fallback har navigatsiyada to'liq sahifani oq qiladi

Sababi: Suspense juda yuqorida (butun ilovani o'raydi). Yechimi: Suspense'ni maqsadli joyga (route ichida yoki og'ir komponent atrofida — 2.6); yoki useTransition (eski kontentni ushlab turish — 2.9).

Xato 5 — use() cheksiz suspend (loading hech tugamaydi)

Sababi: har render yangi Promise yaratildi (use(fetchUser()) — render ichida — 2.12). Yechimi: Promise'ni barqaror qil (useMemo, yoki cache/Query — 12.4).

Xato 6 — Code splitting qildim, lekin tezroq bo'lmadi

Sababi: noto'g'ri narsani bo'lding (kichik komponent), yoki asosiy bundle baribir og'ir. Yechimi: bundle'ni tahlil qil (visualizer — 2.13), eng katta/kam ishlatiladiganni bo'l.

Xato 7 — Lazy komponent SSR'da (Next.js) ishlamaydi

Sababi: React.lazy SSR'da to'g'ridan ishlamaydi 2.14-bob. Yechimi: Next.js'da next/dynamic ishlat 13.10-bob.


7. Integratsiya — bu mavzu stack'ning qayerida uchraydi

  • Modullar 2.14-bob: dinamik import() — statik import'ning asinxron, splitting versiyasi.
  • Vite build (11.3: 2.6): chunk'larni avtomatik yaratadi (content hash bilan keshlanadi).
  • React Router 11.9-bob: route-based splitting (har sahifa lazy — eng keng).
  • Error boundary 11.12-bob: lazy yuklanish xatosini ushlash (production majburiy).
  • Performance 11.11-bob: code splitting — boshlang'ich yuk optimizatsiyasining asosi.
  • Hooks 11.7-bob: preload custom hook (usePreload); useTransition/startTransition (sakrashsiz almashish — 2.9-a).
  • SEO/Core Web Vitals 13.8-bob: kichik boshlang'ich yuk yaxshi LCP.
  • Next.js 13.10-bob: avtomatik splitting, next/dynamic, Server Components.

8. Eng yaxshi amaliyotlar (best practices)

  • Route'larni lazy qil (har sahifa alohida chunk — eng katta foyda — 2.6).
  • Og'ir kutubxona/komponentni bo'l (grafik, muharrir, modal — 2.7); engilini qoldir.
  • Suspense + ErrorBoundary birga (yuklanish + xato — production — 2.8).
  • Skeleton fallback (spinner emas — kam layout shift, yaxshi UX — 2.9).
  • Hover'da preload (kechikishni yo'qotadi — eng yaxshi UX — 2.10).
  • Maqsadli Suspense (butun ilova emas — mustaqil bo'laklar — 2.5).
  • Sahifa almashishda useTransition (eski UI qoladi, sakrash yo'q — 2.9-a).
  • O'lchovga asoslan (bundle visualizer eng kattani bo'l — taxmin emas — 2.13).
  • Juda mayda bo'lma (HTTP overhead — balans — 2.13).
  • Dinamik import og'ir kutubxonaga (jspdf, xlsx — faqat kerak bo'lganda — Misol 1).
  • Next.js'da next/dynamic (SSR muhitida React.lazy o'rniga — 2.14, 13.10).

9. Amaliy loyiha: "Tez Yuklanadigan Ko'p Sahifali Ilova"

Code splitting'ni real ilovaga qo'llab, boshlang'ich yuklanishni o'lchovli kamaytirish.

Maqsad

4-6 sahifali, og'ir komponentlari (grafik, modal) bor ilova yarat va uni code splitting bilan optimallashtir — boshlang'ich bundle'ni keskin kichraytir.

Talablar (requirements)

  1. Route-based splitting: har sahifa lazy + Suspense (Misol 3, 2.6).
  2. Og'ir komponent: grafik yoki xarita kabi og'ir komponentni alohida lazy qil (Misol 4, 2.7).
  3. Dinamik import: og'ir kutubxonani (PDF/Excel) faqat tugma bosilganda yukla (Misol 1, 2.3).
  4. Modal lazy: kam ochiladigan modalni lazy qil (Misol 10, 2.7).
  5. Error boundary: barcha lazy'ni ErrorBoundary bilan o'ra (Misol 5, 2.8).
  6. Skeleton: spinner emas, skeleton fallback ishlat (Misol 6, 2.9).
  7. Preload: navigatsiya havolalariga hover preload qo'sh (Misol 7, 2.10).
  8. Bundle tahlil: rollup-plugin-visualizer bilan o'lcha — qaysi chunk qancha 2.13-bob.
  9. O'lchov: splitting'dan oldin/keyin boshlang'ich bundle hajmini solishtir (DevTools Network).
  10. Balans: kichik komponentlarni bo'lma (faqat katta/og'ir/kam ishlatiladigan — 2.13).

Maslahatlar (hint)

  • Avval npm run build qil va dist/assets chunk hajmlarini ko'r (splitting'dan oldin/keyin).
  • Suspense'ni butun ilovaga emas, route va og'ir komponentga maqsadli qo'y 2.5-bob.
  • ErrorBoundary'ni lazy bilan birga ishlat — deploy'dan keyin eski chunk muammosi uchun (Xato 3).
  • Skeleton — kontent shakliga mos qil (karta uchun karta-skeleton — 2.9).
  • Hover preload — eng sezilarli UX yaxshilanishi (Misol 7).
  • Bundle visualizer ko'rsatgan eng katta/kam ishlatiladiganni bo'l (dalilga asoslan — 2.13).

"Tayyor" mezonlari (acceptance criteria)

  • Har route alohida chunk (Network'da ko'rinadi).
  • Og'ir komponent/kutubxona alohida yuklanadi (faqat kerak bo'lganda).
  • Barcha lazy ErrorBoundary + Suspense bilan o'ralgan.
  • Skeleton fallback (spinner emas).
  • Hover preload ishlaydi (Network'da hover'da chunk yuklanadi).
  • Boshlang'ich bundle splitting'dan keyin sezilarli kichik (o'lchangan).
  • Bundle visualizer bilan tahlil qilingan.
  • Mayda komponentlar bo'linmagan (balans saqlangan).

Yechim kodi ataylab berilmagan — bu loyihani o'zingiz yozib ko'ring.


10. Xulosa va keyingi bobga ko'prik

Bu bobda ilovani tez yuklash texnikasini o'rgandik:

  • Muammo (bundle hajmi, sekin boshlang'ich yuk — 2.1); code splitting (chunk, on-demand — 2.2); dinamik import() (poydevor — 2.3).
  • React.lazy (komponentni dangasa yuklash — 2.4); Suspense (fallback UI — 2.5); route-based 2.6-bob va component-based 2.7-bob splitting.
  • Error boundary bilan (yuklanish xatosi — 2.8); loading holatlari (skeleton — 2.9); useTransition (sakrashsiz almashish — 2.9-a); preload 2.10-bob; named export 2.11-bob; Suspense data fetching (use() — 2.12); o'lchov va vendor split 2.13-bob; SSR/Next.js 2.14-bob.

Endi siz katta ilovani kichik, tez yuklanadigan bo'laklarga bo'lib, faqat kerakli kodni kerakli paytda yuklay olasiz — bu yuklanish tezligini (va foydalanuvchi tajribasini) keskin oshiradi. Eng muhimi — buni o'lchovga asoslanib qilasiz (taxminga emas).

Keyingi bob — 11.9-bob: Routing (React Router). Code splitting'ni route bilan ko'rdik — endi routing'ning o'zini chuqur o'rganamiz. React Router — SPA'da sahifalar orasida (sahifa qayta yuklanmasdan) navigatsiya qilish standarti: route'larni e'lon qilish, <Link> bilan o'tish, dinamik parametrlar (/user/:id), ichma-ich (nested) route'lar, himoyalangan (protected) route'lar, dasturiy navigatsiya (useNavigate), va URL bilan ishlash (useParams, useSearchParams). Bu — har ko'p sahifali React ilovasining skeletini tashkil qiladi.


Foydalanilgan rasmiy/ishonchli manbalar

  • React rasmiy hujjati (react.dev) — lazy, <Suspense>, use, useTransition, startTransition; "Suspense for Data Fetching"
  • web.dev — "Code splitting with dynamic import()", "Reduce JavaScript payloads with code splitting", Core Web Vitals (LCP, CLS)
  • React Router (reactrouter.com) — lazy routes, navigatsiya va transition
  • Vite (vite.dev) va Rollup (rollupjs.org) — dynamic import, build chunks, manualChunks, rollup-plugin-visualizer
  • Next.js (nextjs.org) — next/dynamic, automatic code splitting, Server Components (13.10)
  • MDN — dynamic import(), JavaScript modullar

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
11.8-bob: React.lazy, Suspense va code splitting — Wisar