Vuejs v-pour très lent sur le défilement infini
P粉301523298
P粉301523298 2023-12-28 18:00:08
0
1
390

J'ai un effet vuejs étrange, lorsque j'ajoute de nouvelles données d'objet, v-for restitue tous les objets, même s'ils ont déjà été rendus.

J'implémente le défilement infini comme Facebook.

Code

Pour expliquer ce code, je récupère de nouvelles données de Firebase, puis je les transmets dans un objet de données lorsqu'elles atteignent le bas de l'écran


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>


Maintenant, voici le truc…

Regardez cet enregistrement d'écran, souris focalisée, c'est tellement lent, je ne peux même pas cliquer sur les boutons pendant que vuejs ajoute et charge de nouvelles données

C'est le code que j'utilise

Qu'est-ce que je soupçonne

Je soupçonne qu'à chaque fois que de nouvelles données sont ajoutées, VueJS restitue toutes les données, provoquant cet effet. Comment forcer vueJS à ne pas restituer les données déjà restituées à l'écran ?

P粉301523298
P粉301523298

répondre à tous(1)
P粉567112391

Vous avez deux IIFE asynchrones inutiles ; le second dans forEach est particulièrement problématique car le code asynchrone qu'il contient sera exécuté simultanément à chaque itération de boucle, ce qui a les effets suivants :

  1. getDocs() se déclenchera immédiatement à chaque itération de boucle, envoyant éventuellement du spam au serveur (en supposant qu'il s'agit d'une requête réseau). Est-ce votre intention ? Il semble que vous ne puissiez recevoir qu'un maximum de 5 nouveaux messages, donc c'est probablement bien.
  2. La fonction asynchrone met à jour un certain état, ce qui déclenchera le nouveau rendu de Vue pour chaque document. Cela doit être regroupé à la fin afin que Vue se mette à jour le moins possible.

N’utilisez pas var;使用 constlet 代替。几乎没有充分的理由再使用 var non plus, laissez-le mourir.

Je ne peux pas dire que cela améliorera significativement vos performances, mais je recommande les changements suivants (non testés) :

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);
    }
  }
}

Exécution dans le modèle :post-content="truncateString(linkify(post.data().post_content))" 意味着 linkify 将在每次重新渲染期间执行。我怀疑 linkify Peut-être lente pour les longues listes ? Peut-il être pré-calculé pour chaque poste à l’avance ?

Lorsque le composant est installé, vous enregistrez un écouteur d'événement de défilement de fenêtre. Si le composant est détruit, vous devez désenregistrer l'écouteur d'événements, sinon il se déclenchera toujours à chaque défilement de la fenêtre. Cela ne pose peut-être pas de problème dans votre cas, mais pour les composants réutilisables, c'est nécessaire.

Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal