Vue虚拟列表滚动时为什么会出现空白或闪烁?

皇甫予曦 阅读 4

我用 Vue3 + Composition API 实现了一个虚拟列表,数据量大概有 10 万条。但滚动的时候经常出现大片空白,或者内容闪一下才出来,体验特别差。我已经用了 transform 做位移,也固定了 item 高度,但问题还是存在。

这是我的核心渲染逻辑:

<template>
  <div ref="containerRef" class="list-container" @scroll="handleScroll">
    <div :style="{ height: totalHeight + 'px' }">
      <div
        v-for="item in visibleItems"
        :key="item.id"
        :style="{ transform: translateY(${item.offset}px) }"
        class="list-item"
      >
        {{ item.text }}
      </div>
    </div>
  </div>
</template>
我来解答 赞 2 收藏
二维码
手机扫码查看
1 条解答
♫金利
♫金利 Lv1
这个问题太经典了,十有八九是滚动事件触发太频繁导致的。每次滚动都重新计算 visibleItems,Vue 的响应式系统根本来不及处理,就会出现空白或闪烁。

核心问题在于你的 handleScroll 每次 scroll 事件都直接更新了响应式数据,这会导致高频率的重新渲染。解决方案是用 requestAnimationFrame 把更新操作合并到下一帧:

let isUpdating = false

const handleScroll = () => {
if (isUpdating) return

isUpdating = true
requestAnimationFrame(() => {
updateVisibleItems()
isUpdating = false
})
}


或者更简单的方式,用 scroll 事件配合 requestAnimationFrame>,把计算逻辑抽出来:

const handleScroll = () => {
requestAnimationFrame(() => {
const scrollTop = containerRef.value.scrollTop
const startIndex = Math.floor(scrollTop / itemHeight)
const endIndex = startIndex + Math.ceil(containerHeight / itemHeight) + buffer

visibleItems.value = allItems.slice(startIndex, endIndex).map((item, index) => ({
...item,
offset: (startIndex + index) * itemHeight
}))
})
}


还有个容易忽略的点:你的容器需要设置 overflow: hidden 吗?外部容器的高度也要定死,不然计算可视区域会出问题。

另外,v-for 里的 keyitem.id 是对的,但如果数据有变动(比如排序、筛选),key 可能会乱掉,这时候改成 index 反而更稳,不过一般用 id 就行。

试试加上 requestAnimationFrame 包装,90% 的闪烁问题都能解决。
点赞
2026-03-14 00:01