Series: JavaScript Basics Lesson 94

Hydration

Hydration nima, SSR'dan kelgan 'o'lik' HTML'ga JS qanday ulanishi, mount() bilan farqi, Hydration mismatch xatosi va uning yechimi, vnode va real DOM'ning node-by-node solishtirilishi, addEventListener'ning V8/brauzer darajasida arzon bo'lishi, va Selective/Progressive Hydration.

94-dars

Hydration

Tasavvur qiling, sizga tayyor rasm (poster) berishdi — chiroyli, to'liq ko'rinadi. Lekin bu — shunchaki rasm, unga bosib bo'lmaydi, tugmalari ishlamaydi.

Endi kimdir shu rasmning orqasiga kelib, har bir tugmaga simlarni ulab chiqadi — endi bosganda haqiqatan biror narsa sodir bo'ladi.

Bu jarayon — Hydration ("sug'orish", "jonlantirish"). SSR'dan kelgan jonsiz, lekin ko'rinadigan HTML'ga JavaScript "ulanadi" va uni jonli qiladi.


1. Qanday ishlaydi

Oldingi mavzuda ko'rgan SSR HTML'ni eslang:

<div id="root">
  <button>Bosing: 0</button>
</div>
<script src="bundle.js"></script>

Bu HTML kelgan zahoti — tugma ko'rinadi, "Bosing: 0" deb yozilgan. Lekin uni bossangiz — hech narsa bo'lmaydi. Nega? Chunki bu — brauzerning HTML parser'i yasagan oddiy DOM, unga hech qanday onclick ulangan emas. Server faqat matnni yubordi, funksiyalarni yubora olmaydi (JS funksiyasini HTML orqali "jismoniy" yuborib bo'lmaydi).

bundle.js yuklanib, ishga tushgach, React (yoki Vue) shunday deydi:

"Men bu joyga <button onClick={...}> render qilmoqchi edim. Qarayapman — bu joyda allaqachon <button> bor ekan (SSR yasagan). Men uni qayta yaratmayman, faqat unga onclick funksiyasini ulayman."

// Hydration ichida (soddalashtirilgan tasavvur):
const existingButton = document.querySelector('button'); // mavjudini topadi
existingButton.addEventListener('click', () => {
  count++;
  existingButton.textContent = `Bosing: ${count}`;
});

Muhim: bu — mount() emas. mount() yangi DOM yaratadi (createElement). Hydration esa yangi DOM yaratmaydi — u mavjud DOM'ni "tan oladi" va unga faqat xulq-atvorni (behavior) qo'shadi.


2. Nega bu muhim — agar Hydration bo'lmasa nima bo'lardi

Agar hydration bo'lmasa, React SSR'dan kelgan HTML'ni e'tiborsiz qoldirib, o'zining oddiy CSR usuli bilan ishlaganda edi:

document.getElementById('root').innerHTML = ''; // eskisini o'chiradi
ReactDOM.render(<App />, document.getElementById('root')); // qaytadan yaratadi

Bu — ikki marta ish qilish degani: server bir marta HTML yasadi (vaqt sarfladi), brauzer uni ko'rsatdi, keyin JS xuddi shu narsani yana qayta yaratadi — bu nafaqat sekin, balki ekranda "miltillash" (flicker) ham beradi: foydalanuvchi bir HTML'ni ko'radi, keyin bir zumda u yo'qolib, xuddi shunga o'xshash yangi HTML paydo bo'ladi. Hydration aynan shu ikki barobar ishni oldini oladi.


3. Eng nozik joy — "Hydration mismatch" xatosi

Bu — real loyihalarda eng ko'p uchraydigan Hydration muammosi. Tasavvur qiling:

// Server render qilganda:
function Component() {
  return <p>Vaqt: {new Date().toLocaleTimeString()}</p>;
}

Server HTML yasaganda vaqt — 14:30:05. HTML foydalanuvchiga yetib borguncha, tarmoq kechikishi, JS yuklanishi sabab, bir necha soniya o'tadi. Hydration paytida brauzer o'zi qayta hisoblab, taqqoslaydi:

Server bergan HTML:     <p>Vaqt: 14:30:05</p>
Brauzer hisoblagan:     <p>Vaqt: 14:30:09</p>

Bular mos kelmaydi! React buni sezadi va konsolga ogohlantirish chiqaradi: "Hydration failed because the initial UI does not match what was rendered on the server". Bu — nazariy emas, haqiqiy, kundalik muammo: server va brauzer turli natija chiqaradigan har qanday kod (joriy vaqt, tasodifiy son, window obyektiga bog'liq narsa — chunki serverda window umuman yo'q) — hydration xatosiga olib keladi.

Yechim: bunday "beqaror" qiymatlarni faqat brauzer tomonida, hydration tugagandan keyin hisoblash:

function Component() {
  const [time, setTime] = useState(null); // serverda: null
  useEffect(() => {
    setTime(new Date().toLocaleTimeString()); // faqat brauzerda ishga tushadi
  }, []);
  return <p>Vaqt: {time ?? '...'}</p>;
}

4. V8 va brauzer darajasida — ichki mexanizm

Hydration paytida React (yoki Vue) ikkita daraxtni bir vaqtda, parallel yuradi:

  • Virtual DOM daraxti — React o'zi "men buni render qilishim kerak edi" deb hisoblagan reja (h('button', ...) kabi obyektlar)
  • Haqiqiy DOM daraxti — allaqachon brauzer tomonidan HTML parser orqali yaratilgan, xotiradagi haqiqiy node'lar

Bu ikkalasini tugun-tugun (node-by-node) solishtiradi:

vnode: { tag: 'button', text: 'Bosing: 0' }
DOM:   <button>Bosing: 0</button>

Tag mosmi? → ha (button = button)
Matn mosmi? → ha (Bosing: 0 = Bosing: 0)
     │
     ▼
YANGI DOM YARATILMAYDI. Faqat:
existingButton → internal fiber/vnode strukturasiga "bog'lanadi"
existingButton.addEventListener('click', handler) chaqiriladi

Nega bu V8 nuqtai nazaridan arzon: addEventListener chaqirish — bu faqat brauzerning ichki listener ro'yxatiga (C++ darajasidagi struktura, DOM node'ning o'ziga tegishli) yozuv qo'shish, xolos. Bu — hech qanday layout, paint yoki reflow'ni ishga tushirmaydi (chunki elementning ko'rinishi, o'lchami, joylashuvi — o'zgarmayapti, faqat unga "xulq-atvor" ulanyapti). Solishtirsak: document.createElement('button') + appendChild — bu yangi node qo'shish, ya'ni Layout bosqichini albatta qayta ishga tushiradi.

Shuning uchun Hydration — CSR'dagi to'liq mount()ga qaraganda ancha arzon: u faqat event listener ulash (arzon) qiladi, DOM qurish (qimmat) qilmaydi.


5. Muhim muammo — Hydration ham "bepul" emas

Diqqat: garchi Hydration DOM yaratmasa-da, u baribir:

  • Butun component daraxtini bir marta yugurib chiqishi kerak (har bir komponent funksiyasini chaqirib, "men nima render qilardim" deb solishtirish uchun)
  • Bu — katta sahifalarda (minglab komponent) sezilarli vaqt olishi mumkin — bu vaqt oralig'ida sahifa ko'rinadi, lekin hali to'liq interaktiv emas

Shu muammoni hal qilish uchun zamonaviy React'da Selective/Progressive Hydration degan texnika bor: butun sahifani bir vaqtda hydrate qilish o'rniga, foydalanuvchi qaysi qismga tegsa yoki qaysi qism ekranga ko'rinsa, aynan o'sha qism birinchi navbatda hydrate qilinadi (masalan, footer'dagi tugmalar — foydalanuvchi u yerga scroll qilmaguncha kutib turadi).


6. Xulosa jadvali

Bosqich Nima sodir bo'ladi Qimmatmi?
Server HTML yuboradi To'liq, ko'rinadigan, lekin "o'lik" HTML —
Brauzer HTML parse qiladi DOM tree quriladi (C++ darajasida, tez) Arzon
bundle.js yuklanadi va bajariladi V8 kodni parse + execute qiladi O'rtacha-qimmat
Hydration: vnode ↔ DOM solishtiriladi Mos kelsa — DOM node qayta ishlatiladi Arzon
Event listener'lar ulanadi addEventListener — faqat "ro'yxatga yozuv" Arzon
❌ Mos kelmasa (mismatch) React DOM'ni tashlab, qayta yaratadi Qimmat — SSR foydasi yo'qoladi