Code Splitting — dynamic import, lazy, Suspense
Muammodan boshlaylik
Ilovangiz o'sdi: 40 ta sahifa, admin panel, grafiklar kutubxonasi, Markdown muharriri, PDF eksport. npm run build:
dist/assets/index-a1b2c3.js 1,840 kB │ gzip: 560 kB
(!) Some chunks are larger than 500 kB after minification.
Foydalanuvchi bosh sahifani ochadi — brauzer butun ilovani yuklaydi: admin panelni (u admin emas), grafiklarni (bosh sahifada grafik yo'q), PDF eksportni (hech qachon bosmasligi mumkin). Sekin 3G da — bir necha soniya bo'sh ekran.
Yechim — code splitting (kodni bo'laklash): kodni bir nechta faylga bo'lib, har birini faqat kerak bo'lganda yuklash.
Dynamic import
Oddiy (statik) import — fayl yuklanganda darhol:
import { formatlash } from './utils'; // har doim bundle da
Dynamic import — funksiya kabi, kerak bo'lganda; Promise qaytaradi:
async function pdfYuklab() {
const { pdfYasash } = await import('./pdf-eksport'); // faqat tugma bosilganda
pdfYasash(malumot);
}
Vite (va boshqa bundler lar) import() ni ko'rib, pdf-eksport ni alohida faylga (chunk) ajratadi. U faqat import() chaqirilganda tarmoqdan yuklanadi.
dist/assets/index-a1b2c3.js 420 kB ← asosiy
dist/assets/pdf-eksport-d4e5f6.js 310 kB ← faqat kerak bo'lganda
Bu React ga bog'liq emas — oddiy JavaScript. Og'ir kutubxonalar uchun juda foydali:
// Markdown faqat muharrir ochilganda
const { marked } = await import('marked');
// Sana kutubxonasi faqat kerakli tilda
const locale = await import(`./locales/${til}.js`);
React.lazy — komponentni kechiktirib yuklash
Komponentlar uchun — lazy:
import { lazy, Suspense } from 'react';
const Grafik = lazy(() => import('./Grafik'));
function Hisobot() {
const [korsat, setKorsat] = useState(false);
return (
<>
<button onClick={() => setKorsat(true)}>Grafikni ko'rsatish</button>
{korsat && (
<Suspense fallback={<p>Grafik yuklanmoqda...</p>}>
<Grafik />
</Suspense>
)}
</>
);
}
lazy(() => import('./Grafik'))— komponent kodi birinchi render da yuklanadi<Suspense fallback>— yuklanayotganda nima ko'rsatish- Fayl default export ga ega bo'lishi kerak
Named export bilan
// Grafik.jsx: export function Grafik() {...} — default emas
const Grafik = lazy(() =>
import('./Grafik').then((modul) => ({ default: modul.Grafik }))
);
[!WARNING]
lazy()ni komponent ichida chaqirmang — faqat modul darajasida. Komponent ichida har renderda yangi lazy komponent yaratiladi → yangi tur → React uni har safar unmount va mount qiladi (67-dars), state yo'qoladi va kod qayta-qayta "yuklanadi".
Route lar bo'yicha bo'laklash
Eng katta va eng oson yutuq — har bir sahifani alohida chunk qilish (47-darsda ko'rgan edik):
import { lazy, Suspense } from 'react';
import { Routes, Route } from 'react-router-dom';
import Layout from './Layout';
import BoshSahifa from './pages/BoshSahifa'; // eng ko'p ochiladigan — statik
const Katalog = lazy(() => import('./pages/Katalog'));
const Mahsulot = lazy(() => import('./pages/Mahsulot'));
const Savat = lazy(() => import('./pages/Savat'));
const Admin = lazy(() => import('./pages/admin/Admin'));
function App() {
return (
<Routes>
<Route element={<Layout />}>
<Route index element={<BoshSahifa />} />
<Route path="katalog" element={<Katalog />} />
<Route path="mahsulot/:id" element={<Mahsulot />} />
<Route path="savat" element={<Savat />} />
<Route path="admin/*" element={<Admin />} />
</Route>
</Routes>
);
}
// Layout.jsx
function Layout() {
return (
<>
<Header />
<Suspense fallback={<SahifaSkeleton />}>
<Outlet />
</Suspense>
<Footer />
</>
);
}
Suspense ni Outlet atrofiga qo'ysangiz — sahifa almashganda header va footer joyida qoladi, faqat kontent qismida skeleton ko'rinadi.
Qayerga Suspense qo'yish
// ❌ butun ilova atrofida — har sahifa almashganda butun ekran yo'qoladi
<Suspense fallback={<Spinner />}>
<App />
</Suspense>
// ✅ o'zgaradigan qism atrofida — qolgan qism ko'rinib turadi
<Header />
<Suspense fallback={<KontentSkeleton />}>
<Outlet />
</Suspense>
Bir nechta mustaqil lazy qismlar — har biri o'z Suspense i bilan:
<Suspense fallback={<GrafikSkeleton />}>
<Grafik />
</Suspense>
<Suspense fallback={<JadvalSkeleton />}>
<Jadval />
</Suspense>
Grafik tayyor bo'lsa — jadvalni kutmasdan ko'rsatiladi.
Oldindan yuklash (prefetch)
Lazy yuklash kamchiligi — foydalanuvchi bosganda kutadi. Buni yashirish mumkin:
const adminImport = () => import('./pages/admin/Admin');
const Admin = lazy(adminImport);
<Link to="/admin" onMouseEnter={adminImport} onFocus={adminImport}>
Admin panel
</Link>
Sichqoncha havolaga yaqinlashganda kod yuklana boshlaydi. Bosish paytida — allaqachon keshda. import() ning natijasi brauzer tomonidan keshlanadi — ikki marta chaqirish ikki marta yuklamaydi.
Yuklash xatolari
Yangi versiya deploy qilindi, eski chunk lar serverdan o'chirildi. Eski versiyadagi foydalanuvchi sahifani bosadi — import() 404 bilan xato beradi: "Failed to fetch dynamically imported module".
<ErrorBoundary fallback={<p>Sahifani yuklab bo'lmadi. <button onClick={() => location.reload()}>Yangilash</button></p>}>
<Suspense fallback={<SahifaSkeleton />}>
<Outlet />
</Suspense>
</ErrorBoundary>
Suspense faqat kutishni boshqaradi, xatolarni — Error Boundary (79-dars). Lazy komponentlar atrofida doim ikkalasi bo'lsin.
Nimani bo'laklash kerak
| ✅ Bo'laklash foydali | ❌ Foydasiz |
|---|---|
| Sahifalar (route lar) | Har bir kichik komponent |
| Admin panel, kabinet — faqat ba'zi foydalanuvchilarga | Birinchi ekranda darhol ko'rinadigan narsa |
| Og'ir kutubxonalar: grafiklar, muharrirlar, xaritalar, PDF | Kichik util funksiyalar |
| Modal va dialog oynalar (ochilmasligi mumkin) | Header, navigatsiya |
| Kamdan-kam ishlatiladigan funksiyalar: eksport, import |
[!TIP] Juda mayda bo'laklash ham zararli: har bir chunk — alohida tarmoq so'rovi. 100 ta 2 KB li fayl bitta 200 KB li fayldan sekinroq yuklanishi mumkin. Route lar va og'ir kutubxonalar — ko'p hollarda yetarli. Natijani bundle tahlili (72-dars) bilan tekshiring.
Nima uchun shunday?
Nima uchun bundler lar kodni avtomatik bo'laklamaydi — qaysi kod qachon kerakligini o'zi aniqlasa bo'lmaydimi?
Bundler kodning tuzilmasini ko'radi, lekin foydalanuvchining xatti-harakatini bilmaydi. U Admin komponenti import qilinganini ko'radi, lekin uni foydalanuvchilarning 1% i ochishini bilmaydi. U grafiklar kutubxonasini ko'radi, lekin grafik faqat tugma bosilganda chiqishini bilmaydi. Bu qarorlar — mahsulot haqidagi bilim, va uni faqat dasturchi beradi. import() — bu bundler ga aniq signal: "bu yerda chegara bor, bu kod keyinroq kerak bo'ladi". Shundan keyin bundler qolgan ishni — bog'liqliklarni ajratish, umumiy kodni alohida chunk ga chiqarish, fayl nomlariga hash qo'shish — avtomatik bajaradi.
Xulosa
Eslab qoling:
- Code splitting — kodni chunk larga bo'lib, kerak bo'lganda yuklash; boshlang'ich yuklash tezlashadi
await import('./modul')— har qanday JS uchun; og'ir kutubxonalar uchun foydalilazy(() => import('./Komponent'))— default export; faqat modul darajasida<Suspense fallback>— yuklanayotganda; o'zgaradigan qism atrofida, butun ilova emas- Eng katta yutuq — route lar bo'yicha bo'laklash
- Prefetch —
onMouseEnterdaimport()ni chaqirish - Yuklash xatolari uchun Error Boundary
- Juda mayda bo'laklamang; natijani tahlil bilan tekshiring
Keyingi dars
70-dars: Virtualization — react-virtual, katta ro'yxatlar