Vuejs v-for 在无限滚动时非常滞后
P粉301523298
P粉301523298 2023-12-28 18:00:08
0
1
394

我有一个奇怪的 vuejs 效果,当我添加新的对象数据时,v-for 会重新渲染所有对象,即使它已经渲染了。

我正在实现像 face book 一样的无限滚动。

代码

为了解释这段代码,我从 firebase 获取新数据,然后当数据到达屏幕底部时将其推送到数据对象中


var vueApp = new Vue({

    el: '#root',
    data: {
        postList: [],
        isOkaytoLoad: false,
        isRoomPostEmpty: false,
    },
    mounted: function() {
        // Everytime user scroll, call handleScroll() method
        window.addEventLis tener('scroll', this.handleScroll);
    },
    methods: {
        handleScroll: function()
        {
            var d = document.documentElement;
            var offset = d.scrollTop + window.innerHeight;
            var height = d.offsetHeight - 200;
            
            // If the user is near the bottom and it's okay to load new data, get new data from firebase
            if (this.isOkaytoLoad && offset >= height)
            {
                this.isOkaytoLoad = false;
                (async()=>{
                    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);
                    roomSnapshot.forEach((doc) => {
                        const postID = doc.id;
                        (async()=>{
                            // Put the new data at the postList object
                            this.postList = [...this.postList, doc];
                            const q = query(collection(db, 'comments'), where('post_id', '==', postID));
                            const commentSnapshot = await getDocs(q);
                            doc['commentCount'] = commentSnapshot.size;
                            //this.postList.push(doc);
                            console.log(this.postList);
                            setTimeout(()=>{ this.isOkaytoLoad = true }, 1000);
                        })();
                    });
                    
                    
                })();
            }
        }
    }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>

<div v-if="postList.length > 0" class="card-containers">
   <!-- I have a component `Postcard` in my js file and it works well -->
   <Postcard 
      v-for="post in postList" 
      :key="post.id" 
      :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>


现在,问题就在这里......

看看这个屏幕记录,聚焦鼠标,它很滞后,当 vuejs 添加和加载新数据时我什至无法单击这些按钮

这是我使用的代码

我怀疑什么

我怀疑每次添加新数据时,VueJS 都会重新渲染所有数据,从而产生这种效果。如何强制 vueJS 不重新渲染那些已经在屏幕上渲染的数据?

P粉301523298
P粉301523298

全部回复(1)
P粉567112391

你有两个不必要的异步IIFEforEach 中的第二个问题尤其严重,因为其中的异步代码将在每个循环迭代中同时执行,这会产生以下影响:

  1. getDocs() 将在每次循环迭代时立即触发,可能会向服务器发送垃圾邮件(假设这是执行网络请求)。这是你的意图吗?看来您最多只能获取 5 个新帖子,所以这可能没问题。
  2. 异步函数更新一些状态,这将触发 Vue 为每个文档重新渲染。这应该在最后批量处理在一起,以便 Vue 尽可能减少更新。

也不要使用 var;使用 constlet 代替。几乎没有充分的理由再使用 var 了,让它死吧。

我不能说这会显着提高您的性能,但我建议进行以下更改(未经测试):

async handleScroll() {
  const d = document.documentElement;
  const offset = d.scrollTop + window.innerHeight;
  const height = d.offsetHeight - 200;
  
  // If the user is near the bottom and it's okay to load new data, get new data from firebase
  if (this.isOkaytoLoad && offset >= height) {
    // Prevent loading while we load more posts
    this.isOkaytoLoad = false;

    try {
      // Get new posts
      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);

      // Fetch comments of each post. Do this all at once for each post.
      // TODO: This can probably be optimized into a single query
      // for all the posts, instead of one query per post.
      await Promise.all(roomSnapshot.docs.map(async doc => {
        const postID = doc.id;
        const q = query(collection(db, 'comments'), where('post_id', '==', postID));
        const commentSnapshot = await getDocs(q);
        doc.commentCount = commentSnapshot.size;
      }));

      // Append the new posts to the list
      this.postList.push(...roomSnapshot.docs);
    } catch (ex) {
      // TODO: Handle error
    } finally {
      // Wait a bit to re-enable loading
      setTimeout(() => { this.isOkaytoLoad = true }, 1000);
    }
  }
}

在模板中执行 :post-content="truncateString(linkify(post.data().post_content))" 意味着 linkify 将在每次重新渲染期间执行。我怀疑 linkify 对于长列表可能会很慢?可以提前为每个帖子预先计算吗?

当组件安装时,您正在注册一个窗口滚动事件侦听器。如果组件被销毁,您需要取消注册事件侦听器,否则每当窗口滚动时它仍然会触发。对于您的情况来说,这可能不是问题,但对于可重用组件来说,必须这样做。

热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板