搜索

首页  >  问答  >  正文

Vuejs v-for 在无限滚动时非常滞后

我有一个奇怪的 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粉301523298P粉301523298407 天前438

全部回复(1)我来回复

  • P粉567112391

    P粉5671123912023-12-29 16:34:20

    你有两个不必要的异步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 对于长列表可能会很慢?可以提前为每个帖子预先计算吗?

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

    回复
    0
  • 取消回复