Lazy Loading
Lazy loading strategiyasi: native loading="lazy", IntersectionObserver, dynamic import() bilan code splitting, V8'ning ichki lazy parsing'idan farqi, va CLS/ortiqcha lazy qilish kabi xavf-xatarlar.
68-dars
Lazy Loading
Lazy loading (dangasa yuklash) — resursni (rasm, JS moduli, komponent, ma'lumot) sahifa ochilishi bilan darhol emas, balki haqiqatan kerak bo'lgan paytda (masalan, foydalanuvchi shu qismga scroll qilganda, yoki shu bo'limga o'tganda) yuklash strategiyasi.
Bunga qarama-qarshi tushuncha — eager loading (ishtiyoqli yuklash): sahifa ochilishi bilan hamma narsa darhol yuklanadi, foydalanuvchi ularni ko'rmasa ham.
<!-- Eager — sahifa ochilishi bilan barcha 50 ta rasm yuklanadi -->
<img src="rasm1.jpg" />
<img src="rasm2.jpg" />
<!-- ... 50 ta rasm ... -->
<!-- Lazy — brauzer o'zi kerak bo'lganda yuklaydi -->
<img src="rasm1.jpg" loading="lazy" />
<img src="rasm2.jpg" loading="lazy" />
1. Nega bu kerak?
Sahifa yuklanish jarayonida brauzer cheklangan tarmoq bandligi va vaqt bilan ishlaydi. Agar sahifada 50 ta rasm, 10 ta katta JS moduli bo'lsa-yu, foydalanuvchi faqat sahifaning **yuqori qismi (above the fold)**ni ko'rsa — qolgan 45 ta rasmni darhol yuklash:
- Tarmoq bandligini behuda sarflaydi — ayniqsa sekin internetda foydalanuvchi uchun sezilarli
- Initial load vaqtini (sahifa birinchi bo'lib ko'rinadigan vaqtini) oshiradi — bu Core Web Vitals ko'rsatkichlaridan biri, **LCP (Largest Contentful Paint)**ga bevosita ta'sir qiladi
- JS bundle hajmini kattalashtiradi — barcha kod (hatto foydalanuvchi hech qachon ochmaydigan sahifa/modal uchun kod ham) bitta faylga yig'ilib, parse va compile qilinishi kerak bo'ladi (buni pastda V8 darajasida ko'ramiz)
Lazy loading — "faqat kerak bo'lganda yukla" tamoyili orqali shu muammoni yechadi.
2. Turlari
1. Rasm/video lazy loading (native brauzer qo'llabi)
<img src="rasm.jpg" loading="lazy" alt="..." />
<iframe src="video.html" loading="lazy"></iframe>
Brauzer o'zi rasmni viewport'ga yaqinlashguncha (odatda bir necha yuz piksel oldin) yuklamay turadi. Bu — 2019-yildan boshlab barcha zamonaviy brauzerlarda qo'shimcha JS'siz ishlaydigan native imkoniyat.
2. Intersection Observer orqali qo'lda lazy loading
Eski brauzerlar yoki murakkabroq mantiq (masalan, "elementga 200px qolganda yuklash") kerak bo'lsa:
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src; // haqiqiy manzilni endi qo'yamiz
observer.unobserve(img); // kuzatishni to'xtatamiz
}
});
}, { rootMargin: '200px' }); // viewport'ga 200px qolganda ishga tushsin
document.querySelectorAll('img[data-src]').forEach((img) => {
observer.observe(img);
});
IntersectionObserver — brauzerning native, asinxron API'si (Web API'lar oilasidan) — elementning viewport bilan kesishishini samarali kuzatadi, scroll hodisasiga throttle qo'yib qo'lda tekshirishdan ancha samarali, chunki brauzerning o'z rendering pipeline'i bilan integratsiyalashgan (har scroll'da JS thread'ni band qilmaydi).
3. JS moduli/komponent lazy loading (Code splitting bilan chambarchas bog'liq)
// Eager — modul darhol, boshidanoq yuklanadi
import Chart from './Chart.js';
// Lazy — modul faqat funksiya chaqirilganda yuklanadi
async function grafikniKorsat() {
const { default: Chart } = await import('./Chart.js');
Chart.render();
}
import() — dynamic import (ES2020), oddiy statik importdan farqli — u Promise qaytaradi va modulni runtime'da, alohida tarmoq so'rovi orqali yuklaydi. Bu — masalan, foydalanuvchi modal ochganda, yoki ma'lum sahifaga o'tganda shu qismga tegishli kodni endi yuklash imkonini beradi (React'da React.lazy(), Vue'da defineAsyncComponent — aynan shu mexanizm ustiga qurilgan).
3. V8 darajasida — nega bu faqat "tarmoq" emas, balki dvigatel darajasidagi ham foyda
Ko'pchilik "lazy loading — bu faqat tarmoq trafigini tejash" deb o'ylaydi, lekin bu to'liq rasm emas. V8'da JS kodi tarmoqdan yuklanganidan keyin ham, uni ishlatishdan oldin bir necha bosqichdan o'tkazish kerak:
Fayl yuklandi (network)
│
▼
Parsing (Tokenizer → AST — Abstract Syntax Tree yaratish)
│
▼
Bytecode kompilyatsiyasi (Ignition uchun)
│
▼
Ijro (execution)
Parsing va compilation — bepul emas, bu ham CPU vaqti talab qiladi, ayniqsa katta JS fayllar uchun sezilarli bo'lishi mumkin (bir necha yuz KB — bir necha ming millisekund parsing vaqti bo'lishi mumkin, sekinroq qurilmalarda, masalan arzon mobil telefonlarda).
V8'da bu muammoni yumshatish uchun "lazy parsing" (ichki, avtomatik mexanizm — dasturchi yozgan lazy loading bilan chalkashtirmang, ikkalasi turli daraja) mavjud: V8 funksiya tanasini to'liq parse qilmasdan, faqat uning "chegaralari"ni (qayerdan boshlanib, qayerda tugashini) belgilab qo'yadi — preparsing. Funksiya haqiqatan chaqirilganda, V8 uni to'liq parse qiladi (lazy compilation).
function katta_funksiya() {
// 1000 qator kod, hech qachon chaqirilmasa
}
// V8: "men bu funksiyani ko'rdim, chegarasini bilaman,
// lekin to'liq parse qilib o'tirmayman — faqat chaqirilganda"
Bu — nega dasturchi darajasidagi lazy loading (kodni umuman yuklamaslik) V8'ning o'z ichki lazy parsing'iga qaraganda ko'proq foyda beradi: agar modul umuman tarmoqdan yuklanmasa, V8 hatto "preparsing" ham qilmaydi — chunki fayl hech qachon kelmagan. Ya'ni dasturiy lazy loading — V8 ichki optimallashtirishidan bir bosqich oldinroq turadi: "bu kodni hatto ko'rish ham shart emas" darajasida tejaydi.
4. Xavf-xatarlar (nima uchun "hamma narsani lazy qilib tashlash" ham yechim emas)
1. Layout Shift (CLS — Cumulative Layout Shift)
Agar rasm lazy yuklanayotganda uning o'lchami (width/height) oldindan belgilanmagan bo'lsa, rasm yuklangach sahifa "sakraydi" (chunki brauzer avval rasm uchun joy ajratmagan edi):
<!-- YOMON — o'lcham noma'lum, yuklangach layout siljiydi -->
<img src="rasm.jpg" loading="lazy" />
<!-- YAXSHI — o'lcham oldindan belgilangan, joy ajratib qo'yiladi -->
<img src="rasm.jpg" loading="lazy" width="800" height="600" />
2. Ortiqcha "lazy" qilish — kechikish yaratadi
Agar foydalanuvchi darhol ko'radigan (above-the-fold) kontentni ham lazy qilib qo'ysangiz, bu aksincha LCP'ni yomonlashtiradi — chunki asosiy kontent endi qo'shimcha kutish (Intersection Observer ishga tushishini, keyin tarmoq so'rovini kutish) bilan yuklanadi. Qoida: faqat foydalanuvchi darhol ko'rmaydigan narsalarni lazy qiling, birinchi ekranni — yo'q.
3. Chaining (zanjirlangan) lazy loading — sekinlik yig'iladi
async function ochish() {
const { Modal } = await import('./Modal.js'); // 1-so'rov
const { Chart } = await import('./Chart.js'); // 2-so'rov (Modal tugagach)
// ...
}
Agar dynamic import'lar ketma-ket (await bilan bittadan) chaqirilsa, ular navbat bilan yuklanadi, umumiy vaqt yig'iladi. Agar ular bir-biriga bog'liq bo'lmasa, Promise.all bilan parallel yuklash tezroq:
const [{ Modal }, { Chart }] = await Promise.all([
import('./Modal.js'),
import('./Chart.js'),
]);
Amaliy xulosa
| Nima | Qachon lazy qilish kerak |
|---|---|
| Rasm/video | Viewport'dan tashqarida bo'lgan barcha rasm |
| Modal/dropdown/tab kontenti | Foydalanuvchi ochmaguncha yuklanmasin |
| Route/sahifa komponenti | Foydalanuvchi o'sha route'ga o'tmaguncha |
| Kutubxona (masalan, chart, PDF viewer) | Faqat kerak bo'lgan sahifada, hammasida emas |
| Above-the-fold kontent (birinchi ekran) | Lazy QILINMASIN — eager bo'lishi kerak |