tanstack-vue-virtual-skilld

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

TanStack/virtual
@tanstack/vue-virtual

TanStack/virtual
@tanstack/vue-virtual

Headless UI for virtualizing scrollable elements in Vue
Version: 3.13.21 (Mar 2026) Deps: @tanstack/virtual-core@3.13.21 Tags: beta: 3.0.0-beta.68 (Oct 2023), latest: 3.13.21 (Mar 2026)
References: Docs — API reference, guides
适用于Vue的可滚动元素虚拟化无头UI组件
版本: 3.13.21(2026年3月) 依赖: @tanstack/virtual-core@3.13.21 标签: beta版:3.0.0-beta.68(2023年10月),最新版:3.13.21(2026年3月)
参考资料: 文档 — API参考、使用指南

API Changes

API变更

This section documents version-specific API changes — prioritize recent major/minor releases.
  • BREAKING:
    useVirtualizer
    — replaces
    useVirtual
    in v3 migration; older positional arguments or v2 option names are no longer supported source
  • BREAKING:
    Ref<Virtualizer>
    return type — v3
    useVirtualizer
    returns a Vue
    Ref
    instead of a raw object; instance methods must be accessed via
    .value
    (e.g.,
    rowVirtualizer.value.getVirtualItems()
    )
  • BREAKING:
    count
    — replaces
    size
    option in v3 migration; using
    size
    will result in zero items being virtualized source
  • BREAKING:
    getScrollElement
    — replaces
    parentRef
    option in v3 migration; must be a function that returns the scrollable element or
    null
    source
  • BREAKING:
    measureElement
    — replaces
    measureRef
    pattern from v2; you must now pass
    virtualizer.value.measureElement
    to the
    ref
    attribute and set
    data-index
    on the element source
  • NEW:
    getTotalSize()
    auto-updates — as of v3.13.13, the virtualizer automatically notifies the framework when the
    count
    option changes, ensuring
    getTotalSize()
    and the UI update correctly without manual workarounds for filtering or search source
  • NEW:
    lanes
    — new in v3; allows dividing the list into multiple columns (vertical) or rows (horizontal) to support grid-like or masonry layouts source
  • NEW:
    gap
    — new in v3; specifies the spacing between items in pixels, removing the need for manual margin or padding calculations source
  • NEW:
    useWindowVirtualizer
    — specialized adapter for window-based scrolling; simplifies configuration when the browser window is the scroll container source
  • NEW:
    scrollMargin
    — allows specifying the offset between the scroll container's start and the beginning of the virtualized list; essential for lists preceded by headers source
  • NEW:
    isRtl
    — built-in support for right-to-left language locales; inverts horizontal scrolling logic when enabled source
  • NEW:
    useScrollendEvent
    — utilizes the native
    scrollend
    event where available to reset
    isScrolling
    state, falling back to a debounced timer if disabled source
  • NEW:
    shouldAdjustScrollPositionOnItemSizeChange
    — provides fine-grained control over scroll position adjustments when dynamic items differ from their estimated size source
  • NEW:
    useAnimationFrameWithResizeObserver
    — added in v3.13.x; defers ResizeObserver measurement processing to the next animation frame to batch DOM mutations source
Also changed:
isScrollingResetDelay
new in v3 ·
rangeExtractor
now receives
Range
object ·
VirtualItem
adds
lane
property ·
resizeItem
method for manual size overrides ·
enabled
option to pause observers
本部分记录了各版本的API变更内容,请优先关注近期的大版本/小版本更新。
  • 重大变更:
    useVirtualizer
    — 在v3迁移中替代了
    useVirtual
    ;旧的位置参数或v2版本的选项名称将不再被支持 来源
  • 重大变更:
    Ref<Virtualizer>
    返回类型 — v3版本的
    useVirtualizer
    返回Vue的
    Ref
    对象而非原始对象;实例方法必须通过
    .value
    访问(例如:
    rowVirtualizer.value.getVirtualItems()
  • 重大变更:
    count
    — 在v3迁移中替代了
    size
    选项;若使用
    size
    将导致虚拟化元素数量为0 来源
  • 重大变更:
    getScrollElement
    — 在v3迁移中替代了
    parentRef
    选项;必须是一个返回可滚动元素或
    null
    的函数 来源
  • 重大变更:
    measureElement
    — 替代了v2版本的
    measureRef
    模式;现在你必须将
    virtualizer.value.measureElement
    传递给
    ref
    属性,并在元素上设置
    data-index
    来源
  • 新增功能:
    getTotalSize()
    自动更新 — 从v3.13.13版本开始,当
    count
    选项变更时,虚拟化组件会自动通知框架,确保
    getTotalSize()
    和UI能正确更新,无需为过滤或搜索功能手动编写解决方案 来源
  • 新增功能:
    lanes
    — v3版本新增;允许将列表分为多列(垂直方向)或多行(水平方向),以支持网格状或瀑布流布局 来源
  • 新增功能:
    gap
    — v3版本新增;指定元素间的像素间距,无需手动计算外边距或内边距 来源
  • 新增功能:
    useWindowVirtualizer
    — 针对基于窗口的滚动场景的专用适配器;当浏览器窗口为滚动容器时,可简化配置 来源
  • 新增功能:
    scrollMargin
    — 允许指定滚动容器起始位置与虚拟化列表开头之间的偏移量;对于带有头部的列表至关重要 来源
  • 新增功能:
    isRtl
    — 内置对从右到左语言环境的支持;启用后会反转水平滚动逻辑 来源
  • 新增功能:
    useScrollendEvent
    — 在支持的环境中使用原生
    scrollend
    事件来重置
    isScrolling
    状态;若不支持则回退到防抖定时器 来源
  • 新增功能:
    shouldAdjustScrollPositionOnItemSizeChange
    — 当动态元素与预估尺寸不同时,提供对滚动位置调整的精细控制 来源
  • 新增功能:
    useAnimationFrameWithResizeObserver
    — v3.13.x版本新增;将ResizeObserver的测量处理延迟到下一动画帧,以批量处理DOM变更 来源
其他变更:
isScrollingResetDelay
在v3版本新增 ·
rangeExtractor
现在接收
Range
对象 ·
VirtualItem
新增
lane
属性 ·
resizeItem
方法用于手动覆盖尺寸 ·
enabled
选项用于暂停观察者

Best Practices

最佳实践

  • Account for
    scrollMargin
    in absolute positioning — when using a shared scroll container with static headers, subtract the margin from the item's start position to maintain correct layout source
vue
<script setup>
const rowVirtualizer = useVirtualizer({
  count: 1000,
  scrollMargin: 100, // Height of header
  // ...
})
</script>

<template>
  <div v-for="item in rowVirtualizer.getVirtualItems()" :key="item.key"
    :style="{
      transform: `translateY(${item.start - rowVirtualizer.options.scrollMargin}px)`
    }"
  >
    {{ item.index }}
  </div>
</template>
  • Overestimate
    estimateSize
    for dynamic elements — providing a "maximum likely" size prevents the scrollbar from jumping and items from "resetting" their position when scrolling upwards into unmeasured territory source
  • Implement
    shouldAdjustScrollPositionOnItemSizeChange
    for chat/messaging UIs — use this callback to control scroll adjustments when prepending items, preventing visual jumps as new elements are measured source
  • Attach
    data-index
    when using
    measureElement
    — the virtualizer requires this attribute on the measured DOM element to correctly map the size back to the item's internal state source
vue
<div
  v-for="item in virtualizer.getVirtualItems()"
  :key="item.key"
  :data-index="item.index"
  :ref="virtualizer.measureElement"
>
  {{ item.index }}
</div>
  • Pass configuration via
    computed
    or
    Ref
    — the Vue adapter's
    useVirtualizer
    watch-triggers
    setOptions
    automatically, allowing the instance to reactively update
    count
    or
    overscan
    without manual re-instantiation
  • Avoid
    useAnimationFrameWithResizeObserver
    for performance — native
    ResizeObserver
    is already batched; enabling this adds a ~16ms delay that can cause visual flickering or stale measurements during fast scrolls source
  • Provide a stable
    getItemKey
    for persistent state — using a unique identifier (like a database ID) instead of the default index ensures that item state (focus, internal refs) is preserved during reorders or filtering source
  • Wrap initial
    scrollToIndex
    in
    requestAnimationFrame
    — for "scroll-to-bottom" initialization (e.g. chat), deferring the scroll ensures the DOM is rendered and initial measurements are processed by the virtualizer source
  • Use built-in
    gap
    over manual CSS margins — the
    gap
    option ensures the virtualizer accounts for item spacing in its internal
    getTotalSize()
    calculation, which manual margins do not source
  • Pause observers with
    enabled: false
    — instead of unmounting the virtualizer, toggle the
    enabled
    option to pause monitoring (e.g., when a tab is hidden). This preserves existing measurements while saving CPU cycles source
  • 绝对定位时需考虑
    scrollMargin
    — 当使用带有静态头部的共享滚动容器时,需从元素的起始位置中减去该边距以保持布局正确 来源
vue
<script setup>
const rowVirtualizer = useVirtualizer({
  count: 1000,
  scrollMargin: 100, // 头部高度
  // ...
})
</script>

<template>
  <div v-for="item in rowVirtualizer.getVirtualItems()" :key="item.key"
    :style="{
      transform: `translateY(${item.start - rowVirtualizer.options.scrollMargin}px)`
    }"
  >
    {{ item.index }}
  </div>
</template>
  • 为动态元素高估
    estimateSize
    — 提供一个“最大可能”的尺寸可防止滚动条跳动,以及向上滚动到未测量区域时元素位置“重置”的问题 来源
  • 为聊天/消息类UI实现
    shouldAdjustScrollPositionOnItemSizeChange
    — 使用此回调函数控制添加前置元素时的滚动调整,避免新元素测量时出现视觉跳动 来源
  • 使用
    measureElement
    时附加
    data-index
    — 虚拟化组件需要被测DOM元素上的该属性,才能将尺寸正确映射到元素的内部状态 来源
vue
<div
  v-for="item in virtualizer.getVirtualItems()"
  :key="item.key"
  :data-index="item.index"
  :ref="virtualizer.measureElement"
>
  {{ item.index }}
</div>
  • 通过
    computed
    Ref
    传递配置 — Vue适配器的
    useVirtualizer
    会自动监听并触发
    setOptions
    ,允许实例响应式更新
    count
    overscan
    ,无需手动重新实例化
  • 避免启用
    useAnimationFrameWithResizeObserver
    以提升性能 — 原生
    ResizeObserver
    已支持批量处理;启用此选项会增加约16ms延迟,可能导致快速滚动时出现视觉闪烁或测量数据过期 来源
  • 提供稳定的
    getItemKey
    以保持状态持久化 — 使用唯一标识符(如数据库ID)而非默认索引,确保元素状态(焦点、内部refs)在重新排序或过滤时得以保留 来源
  • 将初始
    scrollToIndex
    包装在
    requestAnimationFrame
    中 — 对于“滚动到底部”的初始化场景(如聊天),延迟滚动可确保DOM已渲染,且虚拟化组件已完成初始测量 来源
  • 使用内置
    gap
    替代手动CSS外边距 —
    gap
    选项可确保虚拟化组件在内部
    getTotalSize()
    计算中考虑元素间距,而手动外边距则无法实现这一点 来源
  • 使用
    enabled: false
    暂停观察者 — 无需卸载虚拟化组件,只需切换
    enabled
    选项即可暂停监控(例如标签页隐藏时)。这样既能保留现有测量数据,又能节省CPU资源 来源