forked from mengyxu/noob-components
3 changed files with 128 additions and 2 deletions
@ -0,0 +1,95 @@ |
|||||||
|
# Virtual Scrolling Debug Notes |
||||||
|
|
||||||
|
## Date: 2026-04-03 |
||||||
|
|
||||||
|
## Issue: Virtual Scrolling Not Updating on Scroll |
||||||
|
|
||||||
|
### Symptoms |
||||||
|
- Rows weren't shifting when scrolling programmatically |
||||||
|
- Debug info showed correct scrollTop but rows stayed the same |
||||||
|
- Force re-render (classList toggle) fixed it momentarily |
||||||
|
|
||||||
|
### Root Causes Found |
||||||
|
|
||||||
|
#### 1. Vue Template Ref Auto-Unwrapping |
||||||
|
**Problem**: `virtualizer.visibleRows` is a `ComputedRef` inside an object. Vue 3 auto-unwraps top-level refs in templates, but NOT refs nested inside objects. |
||||||
|
|
||||||
|
**Evidence**: |
||||||
|
``` |
||||||
|
TypeScript error: Property 'index' does not exist on type 'true | ComputedRefImpl<any> | VirtualRow[]' |
||||||
|
``` |
||||||
|
|
||||||
|
**Fix**: Destructure the needed values from `virtualizer`: |
||||||
|
```typescript |
||||||
|
const { visibleRows, totalHeight: virtualTotalHeight, onScroll } = virtualizer; |
||||||
|
``` |
||||||
|
|
||||||
|
#### 2. Vue 3 Template `.value` Usage |
||||||
|
**Problem**: In Vue 3 `<script setup>`, refs are auto-unwrapped in templates. Using `.value` explicitly on refs returned from a hook (inside an object) causes type inference issues. |
||||||
|
|
||||||
|
**Before (broken)**: |
||||||
|
```vue |
||||||
|
<div v-for="row in virtualizer.visibleRows.value"> |
||||||
|
``` |
||||||
|
|
||||||
|
**After (working)**: |
||||||
|
```vue |
||||||
|
<div v-for="row in visibleRows"> |
||||||
|
``` |
||||||
|
|
||||||
|
#### 3. Overscan + Small Dataset |
||||||
|
**Problem**: With 10 rows (each ~60px = 600px total) and viewport 306px, overscan=5 means all 10 rows are rendered regardless of scroll position. |
||||||
|
|
||||||
|
**Calculation**: |
||||||
|
- Viewport shows ~5 rows |
||||||
|
- Overscan 5 = render rows -5 to +5 from viewport edge |
||||||
|
- 5 + 5 + visible rows = all 10 rows always visible |
||||||
|
|
||||||
|
**Not a bug** - virtual scrolling only benefits when rows > viewport + overscan. |
||||||
|
|
||||||
|
### Key Code Changes |
||||||
|
|
||||||
|
#### list-table-v2.vue |
||||||
|
```typescript |
||||||
|
// Before: all refs nested in object |
||||||
|
const virtualizer = useVirtualRows(...); |
||||||
|
|
||||||
|
// After: destructure for template auto-unwrap |
||||||
|
const { visibleRows, totalHeight: virtualTotalHeight, onScroll } = virtualizer; |
||||||
|
``` |
||||||
|
|
||||||
|
#### useVirtualRows.ts |
||||||
|
```typescript |
||||||
|
// Added safety guard for invalid indices |
||||||
|
const visibleRows = computed<VirtualRow[]>(() => { |
||||||
|
if (startIndex < 0 || endIndex < startIndex) { |
||||||
|
return []; |
||||||
|
} |
||||||
|
// ... rest of computation |
||||||
|
}); |
||||||
|
``` |
||||||
|
|
||||||
|
### Debug Techniques Used |
||||||
|
|
||||||
|
1. **Force re-render**: `table.classList.add('x'); requestAnimationFrame(() => table.classList.remove('x'))` |
||||||
|
- If rows change after force-re-render, reactivity IS working but scroll event isn't triggering updates |
||||||
|
|
||||||
|
2. **Check virtualizer state**: Debug info showed `Visible Rows: 10` and `Total Rows: 10` - all rows were intentionally rendered |
||||||
|
|
||||||
|
3. **Calculate expected behavior**: |
||||||
|
- viewport = 306px |
||||||
|
- total content = 660px (10 rows) |
||||||
|
- max scroll = 354px |
||||||
|
- overscan = 5 on each side |
||||||
|
|
||||||
|
### Lessons Learned |
||||||
|
|
||||||
|
1. In Vue 3 `<script setup>`, always destructure refs that need to be used in templates |
||||||
|
2. Virtual scrolling benefit is only visible when `total_rows > viewport + (overscan * row_height)` |
||||||
|
3. Vue 3 auto-unwraps top-level refs but NOT refs nested in objects |
||||||
|
4. `console.log` in Vue event handlers doesn't appear in browser console - Vue intercepts events |
||||||
|
5. Programmatic scroll (`scrollTop = x`) doesn't fire scroll events - need to manually dispatch |
||||||
|
|
||||||
|
### Files Modified |
||||||
|
- `packages/base/data/list-table-v2/list-table-v2.vue` |
||||||
|
- `packages/base/data/list-table-v2/useVirtualRows.ts` |
||||||
Loading…
Reference in new issue