Vuejs v-für sehr verzögert beim unendlichen Scrollen
P粉301523298
P粉301523298 2023-12-28 18:00:08
0
1
387

Ich habe einen seltsamen VueJS-Effekt: Wenn ich neue Objektdaten hinzufüge, rendert v-for alle Objekte neu, auch wenn sie bereits gerendert wurden.

Ich implementieren unendliches Scrollen wie Facebook.

Code

Um diesen Code zu erklären, erhalte ich neue Daten von Firebase und schiebe sie dann in ein Datenobjekt, wenn es den unteren Bildschirmrand erreicht


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>


Nun, hier ist die Sache…

Schauen Sie sich diese Bildschirmaufnahme an, fokussierte Maus, sie ist so verzögert, dass ich nicht einmal auf die Schaltflächen klicken kann, während vuejs neue Daten hinzufügt und lädt

Dies ist der Code, den ich verwende

Was vermute ich?

Ich vermute, dass VueJS jedes Mal, wenn neue Daten hinzugefügt werden, alle Daten neu rendert, was diesen Effekt verursacht. Wie kann man vueJS dazu zwingen, Daten, die bereits auf dem Bildschirm gerendert wurden, nicht erneut zu rendern?

P粉301523298
P粉301523298

Antworte allen(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 对于长列表可能会很慢?可以提前为每个帖子预先计算吗?

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

Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage