FEMastery
1Level 1 · Fundamentals

Cơ bản nhưng không được mơ hồ

Những điều căn bản không thể thương lượng. Hiểu mơ hồ ở tầng này sẽ gây bug khó lường ở mọi tầng kiến trúc phía trên.

HydrationVirtual DOM diffing complexityEvent loop (macro vs microtasks)Critical rendering pathCode splitting strategiesDynamic import chunkingPreload vs Prefetch vs PreconnectCORS preflightCSRF vs XSS mitigationWeb Workers vs Service Workers
0%
6 phút đọcGắn tương tác vào HTML tĩnh (Hydration)

Hydration

Server gửi HTML tĩnh giúp hiển thị tức thì; client tải JS về để 'tưới nước' (hydrate) - gắn event listener và phục hồi state nội bộ.

Hydration là gì và tại sao nó đắt đỏ?

Khi áp dụng Server-Side Rendering (SSR) hoặc Static Site Generation (SSG), server sẽ sinh sẵn HTML và gửi về cho trình duyệt. Trình duyệt nhanh chóng dựng HTML này lên màn hình, giúp người dùng thấy giao diện gần như ngay lập tức (chỉ số FCP cực tốt). Tuy nhiên, đây chỉ là một 'bức tranh tĩnh' — các nút bấm chưa thể click, các ô nhập liệu chưa thể tương tác vì chưa có mã JavaScript xử lý sự kiện.

Quá trình Hydration (tưới nước) là bước tiếp theo khi client tải xong bundle JavaScript. Framework (như React hoặc Vue) sẽ duyệt qua toàn bộ cây DOM tĩnh đang hiển thị, khớp nối nó với cây DOM ảo (Virtual DOM) được dựng ở phía client, gắn các bộ lắng nghe sự kiện (event listeners) và khôi phục các trạng thái ứng dụng (state). Quá trình này biến trang web tĩnh từ 'đóng băng' thành 'sống động' và có thể tương tác.

Hãy lưu ý: Hydration là một chi phí thuần túy (overhead). Nó không vẽ thêm bất kỳ pixel mới nào lên màn hình, nhưng nó ngốn CPU đáng kể vì phải tải file JS khổng lồ, parse, compile, duyệt cây DOM và chạy logic gắn kết trên Main Thread, trực tiếp ảnh hưởng tiêu cực đến chỉ số thời gian tương tác INP (Interaction to Next Paint).

NÊNLuôn đảm bảo cấu trúc HTML tạo ra từ Server và Client trùng khớp đến từng byte (byte-identical) trong lần render đầu tiên.

CẢNH BÁOTránh xa việc gọi các API chỉ tồn tại trên trình duyệt như `window`, `document`, `localStorage` hoặc các hàm sinh dữ liệu ngẫu nhiên/thời gian (`Math.random()`, `new Date()`) trực tiếp trong chu kỳ render đồng bộ đầu tiên.

Hydration Mismatch và cách khắc phục triệt để

Lỗi Hydration Mismatch xảy ra khi cấu trúc HTML do server sinh ra khác biệt so với cấu trúc mà client tính toán được ở lần render đầu tiên. Khi phát hiện mismatch, React sẽ phải vứt bỏ phần DOM tĩnh bị lệch từ server và tiến hành render lại toàn bộ subtree đó ở phía client. Điều này không chỉ gây lãng phí hiệu năng nghiêm trọng (re-render O(n)) mà còn gây ra hiện tượng giật lag, nhấp nháy giao diện (Layout Shift) rất mất thẩm mỹ.

Để khắc phục, các dữ liệu động chỉ có ở client (như timezone, thông tin đăng nhập trong localStorage, kích thước màn hình) bắt buộc phải được trì hoãn. Chúng ta chỉ nên đọc và cập nhật chúng sau khi component đã được 'mount' thành công lên DOM thực tế (tức là bên trong useEffect hoặc onMounted), lúc này quá trình hydration đã hoàn tất an toàn.

tsx
import React, { useState, useEffect } from 'react';

function DynamicClock() {
  // Khởi tạo state bằng null hoặc giá trị placeholder tĩnh giống hệt server
  const [timeString, setTimeString] = useState<string | null>(null);

  useEffect(() => {
    // Chỉ chạy sau khi component mount thành công trên client-side
    // Luỹ này, hydration đã hoàn tất, cập nhật state động hoàn toàn an toàn
    setTimeString(new Date().toLocaleTimeString());
  }, []);

  return (
    <div className="clock-wrapper">
      Thời gian hiện tại: <span className="time-value">{timeString ?? "—"}</span>
    </div>
  );
}
Mẫu code trì hoãn client-only state bằng useEffect để tránh Hydration Mismatch

VÌ SAOKhi hoãn cập nhật bằng useEffect, lần render đầu tiên ở client vẫn trả về HTML giống hệt server (chứa '—'), giúp hydration thành công mượt mà. Ngay sau đó, useEffect kích hoạt lượt re-render thứ hai để điền dữ liệu thật.