Skip to content
Created: December 20, 2024

The core value of Virtual List/Windowing, The essence is:

👉 Let the browser "think" the list is very large, but in fact there are very few nodes participating in Layout/Paint

1. What exactly is the virtual list “avoiding”? ​

If a virtual list is not used (disaster scene) ​

10,000 条 DOM
↓
每次滚动:
- Layout(全部参与)
- Paint(大量节点)
- Style Recalc

📉 FPS crashes directly

After using virtual list ​

可视区 20 条 + buffer 10 条 = 30 条 DOM
↓
滚动:
- 少量 transform
- 少量 style 更新
- 几乎不 Layout

✔️ Keep visible DOM counts bounded; layout cost still depends on styles, content and updates.

2. Three "pitfall points" of virtual lists (core principles) ​

① DOM quantity control (most important) ​

Complexity of Layout / Paint ≈ Number of DOM

Virtual lists only render:

[ startIndex, endIndex ]

Rest:

  • DOM does not exist
  • Not participating in Render Tree
  • Not participating in Layout/Paint

② Use "displacement" instead of "rearrangement" for scrolling ​

❌ Wrong approach (knowing Layout) ​

js
item.style.top = index * itemHeight + 'px'
  • top → affects layout
  • Each item may trigger layout calculations

✅ Positioning optimization (when compositing conditions hold) ​

js
container.style.transform = `translateY(${offset}px)`
  • Transform positioning usually leaves document-flow layout unchanged.
  • Reusable layers can reduce Layout / Paint, but compositor-only updates are conditional.

The core benefit is limiting how many DOM nodes participate in rendering.

③ Taking place but not rendering (scroll illusion) ​

core structure ​

html
<div class="viewport">
  <div class="phantom"></div>
  <div class="content"></div>
</div>
css
.viewport {
  position: relative;
  overflow-y: auto;
  height: 400px;
}
.phantom {
  height: 100000px; /* 总高度 */
}
.content {
  position: absolute;
  top: 0;
  left: 0;
  transform: translateY(2000px);
}

browser perspective ​

  • The phantom supplies total height and can require Layout when its height or container changes.
  • Content limits visible DOM and uses transform for offsets; changing content can still require paint.

👉 The scroll bars are real, but the DOM is minimal

3. Comparison of complete rendering processes (very important) ​

Normal list scrolling ​

scroll
 → scroll offset / clipping
 → Composite
 → Layout / Paint when content or geometry requires it

Virtual list scrolling ​

scroll
 → JS calculates visible range
 → update or recycle a small number of nodes
 → Layout / Paint as needed
 → transform offset / Composite

Virtualization reduces layout and paint work without guaranteeing their absence.

4. Why does scrolling itself not layout frequently? ​

Browser optimization points ​

  • scroll ≠ recalculate layout

  • Just:

    • Change scroll offset
    • Crop visible area

But the problem is: ​

👉 When there are too many child nodes, Paint and Composite are still expensive

Virtual list directly:

  • don't let them exist

5. What to do with Variable Height? ​

This is an advanced level.

Option 1: Fixed height (fastest) ​

  • Best performance
  • The simplest implementation

Solution 2: Highly cached (mainstream solution) ​

js
heightMap[index] = realHeight
  • first measurement
  • Use cache directly later
  • Avoid repeated getBoundingClientRect

📌 Prevent Layout Thrashing

Solution three: IntersectionObserver (modern solution) ​

  • IntersectionObserver reports intersections, not complete item-height measurements.
  • Use ResizeObserver for size changes and cache measured heights.
  • Batch measurements and updates to avoid forced Layout from interleaved reads/writes.

6. Verify Layout / Paint costs in DevTools ​

1️⃣Performance panel ​

  • During scrolling:

  • Check whether Layout / Paint decreases and DOM updates produce long tasks.

  • Inspect compositing costs rather than inferring performance from transform alone.

2️⃣ Paint flashing ​

  • Very little flicker in the visible area

3️⃣ FPS meter ​

  • Measure actual frame rate; it depends on device, refresh rate, data and application work.

7. Why do React/Vue recommend virtual lists? ​

React(react-window) ​

  • Use transform
  • Merge setState
  • rAF scheduling

Vue(vue-virtual-scroller) ​

  • Absolute positioning + translate
  • cache height
  • Reduce watcher triggers

👉 The essence is:

Controlling the DOM + using compositing layers

8. A minimalist "virtual list core algorithm" (for understanding) ​

js
const itemHeight = 40
const visibleCount = Math.ceil(viewportHeight / itemHeight)

function onScroll(scrollTop) {
  const start = Math.floor(scrollTop / itemHeight)
  const end = start + visibleCount + buffer

  content.style.transform =
    `translateY(${start * itemHeight}px)`

  render(items.slice(start, end))
}

9. Summary ​

  • The virtual list renders only a small amount of DOM near the visible area.
  • Significantly reduce the number of nodes participating in Layout and Paint.
  • Also use transform for displacement,
  • Let the rolling process mainly occur in the Composite phase,
  • This maintains stable rendering performance in lists with large data volumes.