Vue瀑布流长列表渲染卡顿,怎么优化?

A. 付楠 阅读 65

最近在做图片瀑布流页面,用CSS Grid布局配合v-for渲染500条数据,滚动特别卡顿。试过把图片懒加载改成v-lazy,但滑动到中间位置就直接卡死不动了。

代码结构大概是这样写的:


<template>
  <div class="grid" :style="{ '--cols': cols }">
    <div 
      v-for="(item, index) in items" 
      :key="index" 
      class="grid-item"
      :style="{ '--rowSpan': item.rowSpan }"
    >
      <img :src="item.url" @load="onImageLoad(index)">
    </div>
  </div>
</template>

<style>
.grid { display: grid; gap: 10px; grid-template-columns: repeat(var(--cols), 1fr); }
.grid-item { grid-row-end: span var(--rowSpan); }
</style>

试过用计算属性分段渲染,但计算图片实际高度时总出错,动态计算rowSpan的逻辑卡在布局重排上。有没有更好的实现方式或者需要调整的代码细节?

我来解答 赞 8 收藏
二维码
手机扫码查看
1 条解答
南宫世玉
你这个问题我遇到过,核心问题在于两点:一是瀑布流布局在大量DOM节点下的渲染性能瓶颈,二是图片懒加载和动态高度计算导致的主线程阻塞。

你现在的CSS Grid布局加v-for直接渲染500条,浏览器要做大量布局计算。再加上图片加载触发的onImageLoad频繁更新rowSpan,造成频繁重排重绘,性能扛不住很正常。

优化方向:

虚拟滚动必须上。只渲染当前可视区域+缓冲区域的图片,滚动时动态更新。推荐使用[Vue Virtual Scroller](https://github.com/Akryum/vue-virtual-scroller),开箱即用。

图片懒加载不要用v-lazy。原生的IntersectionObserver性能更好,比如这样:
const img = document.querySelector('img')
const io = new IntersectionObserver(entries => {
if (entries[0].isIntersecting) {
img.src = img.dataset.src
io.disconnect()
}
})
io.observe(img)


动态计算rowSpan的问题。你需要等图片实际加载完成后再计算高度,不能直接在onImageLoad里改数据。建议在图片onload之后,用requestIdleCallback来处理布局更新。

CSS方面加点force硬件加速:
.grid {
will-change: transform;
contain: layout;
}


图片预加载和压缩也很关键。确保图片本身是webp格式,而且服务端做了响应式裁剪。

如果还是卡,可以考虑退而求其次用绝对定位模拟瀑布流,用js计算每个item的top/left。虽然麻烦,但比Grid布局在大数据量下更可控。

把这些都改完之后,我估计你500条数据也能顺畅滑动。我之前就是这么搞定的,卡顿问题基本消失。
点赞 6
2026-02-05 09:02