Vuejs v-for 在無限滾動時非常滯後
P粉301523298
P粉301523298 2023-12-28 18:00:08
0
1
393

我有一個奇怪的 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

你有兩個不必要的非同步IIFE; forEach 中的第二個問題尤其嚴重,因為其中的非同步程式碼將在每個循環迭代中同時執行,這會產生以下影響:

  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 對於長列表可能會很慢?可以提前為每個帖子預先計算嗎?

當元件安裝時,您正在註冊一個視窗滾動事件偵聽器。如果元件被銷毀,您需要取消註冊事件偵聽器,否則每當視窗滾動時它仍然會觸發。對於您的情況來說,這可能不是問題,但對於可重複使用元件來說,必須這樣做。

熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板