Lazy Loading — Route larni bo'laklash, Suspense bilan
Muammodan boshlaylik
Ilovangiz o'sib bordi: 20 ta sahifa, har birida o'z komponentlari, kutubxonalari. Foydalanuvchi birinchi marta saytga kirdi.
Brauzer barcha sahifalarni — foydalanuvchi hech qachon ko'rmaydigan sahifalarni ham — bir vaqtda yuklab oladi. Bundle hajmi — 2MB. Yuklash vaqti — 5-8 soniya. Foydalanuvchi yarim yo'lda saytni yopdi.
Muammo: hamma narsani birdaniga yuklash.
Yechim: foydalanuvchi faqat ko'rmoqchi bo'lgan sahifani yuklaydi.
/bosh-sahifa ga kirsa — faqat bosh sahifa kodi yuklanadi.
/mahsulotlar ga o'tsa — o'shanda mahsulotlar kodi yuklanadi.
Bu — Lazy Loading (dangasa yuklash).
Lazy Loading qanday ishlaydi?
Oddiy import — barcha fayllar bir bundleda:
import Dashboard from './pages/Dashboard';
import Mahsulotlar from './pages/Mahsulotlar';
import Sozlamalar from './pages/Sozlamalar';
// Hammasi bitta js faylga yig'iladi — katta bundle
Lazy import — har sahifa alohida chunk (bo'lak):
const Dashboard = lazy(() => import('./pages/Dashboard'));
const Mahsulotlar = lazy(() => import('./pages/Mahsulotlar'));
const Sozlamalar = lazy(() => import('./pages/Sozlamalar'));
// Har biri alohida js fayl — kerak bo'lganda yuklanadi
Vite va webpack bu lazy() chaqiriqlarini ko'rib, har bir import
uchun alohida fayl (chunk) yaratadi. Foydalanuvchi o'sha Route ga
o'tganda — o'sha chunk yuklanadi.
Hayotiy o'xshatma
Restoran menyusi. Oddiy usul: barcha taomlarni darhol tayyorlab qo'yish — isrof. Yaxshi usul: buyurtma kelganda tayyorlash.
Lazy Loading ham shunday: sahifa so'ralganda kodi yuklanadi, oldin emas. Birinchi yuklash tez bo'ladi, keyin har sahifa birinchi marta ochilganda biroz kutiladi — lekin ikkinchi marta tezda ochiladi (keshi ishga tushadi).
React.lazy va Suspense
React.lazy — dinamik import ni komponentga aylantiradi.
Suspense — komponent yuklanayotganda nima ko'rsatilishini belgilaydi.
import { lazy, Suspense } from 'react';
// Lazy import
const Dashboard = lazy(() => import('./pages/Dashboard'));
const Mahsulotlar = lazy(() => import('./pages/Mahsulotlar'));
function App() {
return (
<Suspense fallback={<div>Yuklanmoqda...</div>}>
<Routes>
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/mahsulotlar" element={<Mahsulotlar />} />
</Routes>
</Suspense>
);
}
fallback — komponent yuklanayotganda ko'rsatiladigan UI.
Chunk yuklanib bo'lgach, fallback yo'qoladi va haqiqiy
komponent ko'rsatiladi.
To'liq misol — barcha Route lar lazy
// App.jsx
import { lazy, Suspense } from 'react';
import { Routes, Route } from 'react-router-dom';
import ProtectedRoute from './components/ProtectedRoute';
import YuklanishEkrani from './components/YuklanishEkrani';
// Ochiq sahifalar
const Bosh = lazy(() => import('./pages/Bosh'));
const Kirish = lazy(() => import('./pages/Kirish'));
const TopilmadI = lazy(() => import('./pages/TopilmadI'));
// Himoyalangan sahifalar
const Dashboard = lazy(() => import('./pages/Dashboard'));
const Profil = lazy(() => import('./pages/Profil'));
const Mahsulotlar = lazy(() => import('./pages/Mahsulotlar'));
const MahsulotDetali = lazy(() => import('./pages/MahsulotDetali'));
const Sozlamalar = lazy(() => import('./pages/Sozlamalar'));
function App() {
return (
<Suspense fallback={<YuklanishEkrani />}> {/* ← bir marta, yuqorida */}
<Routes>
<Route path="/" element={<Bosh />} />
<Route path="/kirish" element={<Kirish />} />
<Route element={<ProtectedRoute />}>
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/profil" element={<Profil />} />
<Route path="/mahsulotlar" element={<Mahsulotlar />} />
<Route path="/mahsulot/:id" element={<MahsulotDetali />} />
<Route path="/sozlamalar" element={<Sozlamalar />} />
</Route>
<Route path="*" element={<TopilmadI />} />
</Routes>
</Suspense>
);
}
export default App;
Yaxshi yuklanish ekrani
Oddiy <div>Yuklanmoqda...</div> ishlatmang — foydalanuvchi
sahifa ko'rinishini kutayotganda bo'sh ekran yoki matn ko'radi.
Skeleton UI — sahifaning sxematik ko'rinishi — ancha yaxshiroq:
// components/YuklanishEkrani.jsx
function YuklanishEkrani() {
return (
<div style={{ padding: '24px' }}>
{/* Sarlavha skeleton */}
<div style={{
width: '200px', height: '28px',
background: '#e2e8f0', borderRadius: '4px',
marginBottom: '16px', animation: 'pulse 1.5s infinite'
}} />
{/* Matn satrlari skeleton */}
{[100, 80, 90, 60].map((kenglik, i) => (
<div key={i} style={{
width: `${kenglik}%`, height: '16px',
background: '#e2e8f0', borderRadius: '4px',
marginBottom: '8px', animation: 'pulse 1.5s infinite'
}} />
))}
</div>
);
}
export default YuklanishEkrani;
Har Route uchun alohida Suspense
Bir Suspense barcha Route larni o'rasa — istalgan Route
yuklanayotganda butun sahifa fallback ga almashadi.
Har Route uchun alohida Suspense — faqat o'sha qism yuklanish
ko'rsatadi, qolgan UI o'zgarmasdan turadi:
<Routes>
<Route path="/" element={<Bosh />} />
<Route
path="/dashboard"
element={
<Suspense fallback={<DashboardSkeleton />}>
<Dashboard />
</Suspense>
}
/>
<Route
path="/mahsulotlar"
element={
<Suspense fallback={<MahsulotlarSkeleton />}>
<Mahsulotlar />
</Suspense>
}
/>
</Routes>
Bu murakkabroq, lekin UX (foydalanuvchi tajribasi) yaxshiroq.
Oddiy loyihalarda bitta Suspense yetarli.
Qaysi sahifalarni lazy qilish kerak?
Hammani lazy qilish shart emas. Qoidalar:
Lazy qiling:
- Katta va kam tashriflangan sahifalar — Admin paneli, Hisobotlar
- Og'ir kutubxonalar ishlatadigan sahifalar — grafik, xarita, editor
- Himoyalangan sahifalar — login qilmagan ko'rmaydi
Lazy qilmang:
- Bosh sahifa — foydalanuvchi darhol ko'radi
- Login sahifasi — birinchi ochiladi
- Kichik, tez yuklanadigan komponentlar — ortiqcha chunk yaratish ham yomon
Preloading — oldindan yuklash
Foydalanuvchi havola ustiga sichqoncha olib kelganda — hali bosmagan, lekin bosishi ehtimoli katta. Shu vaqtda chunk ni yuklashni boshlash mumkin:
const Mahsulotlar = lazy(() => import('./pages/Mahsulotlar'));
// Oldindan yuklash funksiyasi
function mahsulotlarniOldinYukla() {
import('./pages/Mahsulotlar'); // ← React.lazy dan mustaqil import
}
function Nav() {
return (
<nav>
<Link
to="/mahsulotlar"
onMouseEnter={mahsulotlarniOldinYukla} // ← hover da yuklash boshlanadi
>
Mahsulotlar
</Link>
</nav>
);
}
Foydalanuvchi havolaga bosguncha chunk yuklanib bo'ladi — sahifa bir zumda ochiladi.
Tez-tez uchraydigan xatolar
1. Suspense siz lazy ishlatish
// ❌ Xato — Suspense yo'q
const Dashboard = lazy(() => import('./pages/Dashboard'));
function App() {
return (
<Routes>
<Route path="/dashboard" element={<Dashboard />} />
</Routes>
// Suspense yo'q — React xato chiqaradi:
// "A React component suspended while rendering, but no fallback UI was specified"
);
}
// ✅ To'g'ri — Suspense o'rab turadi
function App() {
return (
<Suspense fallback={<div>Yuklanmoqda...</div>}>
<Routes>
<Route path="/dashboard" element={<Dashboard />} />
</Routes>
</Suspense>
);
}
Sababi: lazy bilan yuklangan komponent yuklangunga qadar
React "toʻxtatilgan" (suspended) holatda bo'ladi. Suspense
bo'lmasa bu holatni kim boshqarishi noma'lum — React xato chiqaradi.
2. lazy ni komponent ichida chaqirish
// ❌ Xato — har render da yangi lazy yaratiladi
function App() {
const Dashboard = lazy(() => import('./pages/Dashboard')); // ← ichida!
return <Routes>...</Routes>;
}
// ✅ To'g'ri — komponent tashqarisida, modul darajasida
const Dashboard = lazy(() => import('./pages/Dashboard'));
function App() {
return <Routes>...</Routes>;
}
Sababi: lazy komponent ichida bo'lsa, har render da yangi
lazy obyekti yaratiladi. React "bu yangi komponent" deb biladi
va har safar qayta mount qiladi — kesh ishlamaydi, cheksiz yuklash
davom etadi.
3. Default export yo'q
// ❌ Xato — default export yo'q
// pages/Dashboard.jsx
export function Dashboard() { ... } // named export
// App.jsx
const Dashboard = lazy(() => import('./pages/Dashboard'));
// Xato: element type is invalid
// ✅ To'g'ri — default export bor
// pages/Dashboard.jsx
function Dashboard() { ... }
export default Dashboard; // ← default export shart
// Yoki named export ni o'rab olish:
const Dashboard = lazy(() =>
import('./pages/Dashboard').then((m) => ({ default: m.Dashboard }))
);
Sababi: React.lazy faqat default export li modullarni
qo'llab-quvvatlaydi. Named export bo'lsa — .then() bilan
default ga o'girish kerak.
4. Tarmoq xatosini e'tiborsiz qoldirish
Foydalanuvchi chunk yuklanayotganda tarmoq uzilsa — xato ekrani
ko'rsatish kerak. Buning uchun ErrorBoundary ishlatiladi
(79-darsda batafsil):
// Oddiy yechim — lazy import da xatoni ushlash
const Dashboard = lazy(() =>
import('./pages/Dashboard').catch(() => ({
default: () => (
<div>
Sahifa yuklanmadi.{' '}
<button onClick={() => window.location.reload()}>
Qayta urinish
</button>
</div>
),
}))
);
Xulosa
- Lazy Loading — sahifa kodi faqat kerak bo'lganda yuklanadi
React.lazy(() => import('./Sahifa'))— dinamik import<Suspense fallback={...}>— yuklash vaqtida ko'rsatiladigan UIlazymodul darajasida — komponent ichida emas- Komponentlar
default exportbilan eksport qilinishi shart - Bosh sahifa va login sahifasini lazy qilmang
- Preloading —
onMouseEnterda oldindan yuklash - Har Route uchun alohida
Suspense— finer-grained UX
Keyingi dars: 48-dars: Fetch va Axios — React da HTTP so'rovlar