Mục lục

Render 10.000 dòng không lag: Bí mật Virtualization

Kỹ thuật render danh sách hàng vạn phần tử với độ trễ bằng 0 sử dụng Windowing và GPU Acceleration.

Render một danh sách lớn (Large Lists) là một trong những bài toán hiệu năng UI kinh điển nhất. Khi số lượng DOM nodes vượt quá 1,000, trình duyệt bắt đầu gặp khó khăn trong việc tính toán Layout và Repaint, dẫn đến hiện tượng trễ (lag) khi cuộn trang, đặc biệt trên thiết bị di động.

1. Cơ chế Windowing

Windowing (hay Virtualization) hoạt động dựa trên nguyên tắc: Chỉ render những gì người dùng đang nhìn thấy.

Thay vì render 10,000 dòng (<tr> hoặc <div>), chúng ta chỉ render một tập hợp nhỏ (ví dụ: 20 dòng) nằm trong khung nhìn (Viewport), cộng thêm một vùng đệm (Overscan) để đảm bảo trải nghiệm cuộn mượt mà.

Lợi ích kỹ thuật:

  1. Memory Footprint: Giữ số lượng DOM nodes cố định bất kể kích thước dữ liệu (O(1) thay vì O(n)).
  2. GPU Utilization: Ít layer cần composite hơn, giảm tải cho GPU.
  3. Event Handling: Giảm số lượng Event Listeners cần gắn vào DOM.

2. Thư viện tiêu chuẩn: react-window

Được phát triển bởi Brian Vaughn (Core React Team), react-window là giải pháp tối ưu nhất hiện nay cho Virtualization trong hệ sinh thái React.

Implementation Pattern

tsx:
import { FixedSizeList as List } from 'react-window';

// Row Component: Render cực nhẹ, styles inline là bắt buộc để positioning
const Row = ({ index, style }) => (
  <div style={style} className="flex items-center border-b h-[50px]">
    Item {index}
  </div>
);

const VirtualList = () => (
  <List
    height={500}      // Chiều cao viewport
    itemCount={10000} // Tổng số lượng items
    itemSize={50}     // Chiều cao cố định mỗi item
    width="100%"
  >
    {Row}
  </List>
);

Lưu ý kỹ thuật: react-window sử dụng position: absolute để đặt các row. Do đó, bạn cần truyền prop style vào element gốc của Row component.

3. Advanced Techniques

Variable Size List (Dynamic Height)

Trong thực tế, nội dung (như Feed Facebook/Twitter) có chiều cao không đồng nhất.

  • Sử dụng VariableSizeList.
  • Cần một thuật toán hoặc Mapping function để tính toán trước chiều cao của item tại mỗi index.
  • Nếu chiều cao hoàn toàn động (dựa trên text content), cần render "nháp" (off-screen rendering) để đo chiều cao trước khi hiển thị vào list.

Infinite Loading

Kết hợp Virtualization với Pagination.

  • Sử dụng react-window-infinite-loader.
  • Tự động trigger callback loadMoreItems khi user cuộn đến gần cuối danh sách ảo.

4. Engineering Case Study: High-Frequency Financial Dashboard

Trong các ứng dụng giao dịch tài chính (Crypto/Stock Trading), bảng Order Book hiển thị hàng nghìn lệnh đặt mua/bán và giá cả nhảy liên tục (hàng chục lần/giây).

Thách thức: Nếu re-render toàn bộ list mỗi khi giá thay đổi, UI sẽ bị đóng băng (Main thread blocked).

Giải pháp:

  1. Virtualization: Chỉ render 20 lệnh đang nằm trong view.
  2. Memoization: Sử dụng React.memo cho từng Row component với custom comparator arePropsEqual để chỉ update row nào có giá thay đổi.
  3. Throttling: Giới hạn tần suất update xuống 30fps hoặc 60fps thay vì real-time theo WebSocket message để mắt người dùng kịp xử lý.

5. Trade-offs (Sự đánh đổi)

  1. Ctrl+F (Find in Page): Trình duyệt không thể tìm kiếm nội dung chưa được render. (Cần build UI Search riêng).
  2. Accessibility (A11y): Screen readers có thể gặp khó khăn nếu không cấu hình đúng các ARIA attributes.
  3. SEO: Google Bot có thể không thấy toàn bộ nội dung (Giải pháp: Dùng Pagination cho trang public, Virtualization cho App Dashboard).

Kết luận

Virtualization là kỹ thuật bắt buộc cho các ứng dụng Data-Intensive. Tuy nhiên, cần cân nhắc kỹ các đánh đổi về SEO và Accessibility trước khi áp dụng cho các trang public-facing.

Quảng cáo
mdhorizontal