You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vue.js无限滚动时v-for卡顿,新增数据触发全量重渲染如何解决?

问题根因

  • 列表更新频次过高:遍历获取到的帖子数据时,每拿到一条就通过this.postList = [...this.postList, doc]覆盖整个数组,单页拉取5条数据就会触发5次全列表重渲染。
  • 重复触发响应式更新:将文档加入列表后才异步拉取评论数并修改doc.commentCount,已进入响应式数组的对象属性变更会再次触发列表重渲染,进一步增加性能开销。
  • 滚动事件无节流:scroll事件触发频率可达每秒几十次,未做节流处理会占用大量主线程资源,加剧卡顿。

修复方案

1. 优化数据更新逻辑

先批量拉取所有帖子数据、异步并行获取所有帖子的评论数,所有数据处理完成后再一次性更新到postList中,全程仅触发1次列表更新。

2. 新增滚动事件节流

限制滚动事件处理函数的触发频率,降低非必要的计算开销。

3. 可选组件渲染优化

如果已渲染的旧帖子卡片不需要动态更新数据,可在Postcard组件上添加v-once指令,强制组件仅渲染一次,后续更新时跳过旧组件的diff逻辑。

修改后的代码

JavaScript部分

// 先实现节流工具函数
function throttle(func, delay) {
    let lastRun = 0;
    return function() {
        const now = Date.now();
        if (now - lastRun >= delay) {
            lastRun = now;
            func.apply(this, arguments);
        }
    }
}

var vueApp = new Vue({
    el: '#root',
    data: {
        postList: [],
        isOkaytoLoad: false,
        isRoomPostEmpty: false,
    },
    mounted: function() {
        // 给滚动事件加300ms节流
        window.addEventListener('scroll', throttle(this.handleScroll, 300));
    },
    methods: {
        handleScroll: async function() {
            var d = document.documentElement;
            var offset = d.scrollTop + window.innerHeight;
            var height = d.offsetHeight - 200;
            
            if (this.isOkaytoLoad && offset >= height) {
                this.isOkaytoLoad = false;
                const lastPost = this.postList[this.postList.length - 1];
                const room_id = PARAMETERS.get('room');

                const q = query(collection(db, 'posts'), where('room', '==', room_id), orderBy("time", "desc"), limit(5), startAfter(lastPost));
                const roomSnapshot = await getDocs(q);
                // 批量处理所有帖子和评论请求
                const newPostPromises = roomSnapshot.docs.map(async (doc) => {
                    const commentQ = query(collection(db, 'comments'), where('post_id', '==', doc.id));
                    const commentSnapshot = await getDocs(commentQ);
                    // 先给doc加上评论数再加入数组
                    doc.commentCount = commentSnapshot.size;
                    return doc;
                });
                // 等待所有帖子数据处理完成
                const newPosts = await Promise.all(newPostPromises);
                // 一次性把所有新帖子加入列表,仅触发一次更新
                this.postList.push(...newPosts);
                setTimeout(()=>{ this.isOkaytoLoad = true }, 1000);
            }
        }
    }
})

模板部分(可选优化)

<div v-if="postList.length > 0" class="card-containers">
   <Postcard 
      v-for="post in postList" 
      :key="post.id" 
      v-once <!-- 旧卡片不需要更新时添加,可大幅降低重渲染开销 -->
      :post-id="post.id" 
      :owner-name="post.data().owner_displayName" 
      :owner-uid="post.data().owner_id" 
      :post-type="post.data().post_type"
      :image-url="post.data().image_url"
      :post-content="truncateString(linkify(post.data().post_content))"
      :room="post.data().room" 
      :time="post.data().time.toDate()"
      :likers="post.data().likers"
      :comment-count="post.commentCount"
      :file-url="post.data().file_url"
      :file-name="post.data().file_name"
      :downloads="post.data().downloads">
   </Postcard>
</div>

内容的提问来源于stack exchange,提问作者Dev-Raldin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.26 07:06:04