Seriya: JavaScript Basics Dars 68

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