Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Vue.js hiding of items outside current viewport

I'm making an e-commerce type menu in Vue.js, with items which are divs that contain a fair amount of functionality and images. Performance is fairly good when rendering about 200 of these items, but when more than that many are added the site begins to perform sluggishly.

What's the best way to conditionally hide or remove Vue elements from the DOM if they are outside the current scrollable view (like ScrollViews in iOS)? Are there any plugins or libraries that can help with the performance of long lists of data items in Vue.js?

Thanks!

like image 476
T Patrick Avatar asked Feb 05 '23 18:02

T Patrick


1 Answers

I've made a demo snippet using the package I mentioned in my comment.

I've made a "signal" item that acts as the watcher. When the "signal" item leaves the viewport, the "complex-stuff" is no longer rendered. I did it this way so you can see the "complex-stuff" disappear. When the "signal" scrolls back into view, the "complex-stuff" is rendered.

You could just put the watcher on the widget root element and things will only be hidden when the whole widget is out of view. You don't want to put the v-if on the root element, though, or it will never come back once it goes away.

const containerMonitor = scrollMonitor.createContainer(document.body);

new Vue({
  el: '#app',
  data: {
    ids: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
  },
  components: {
    widget: {
      template: '#widget-template',
      props: ['id'],
      data() {
        return {
          visible: true
        };
      },
      mounted() {
        const elementWatcher = containerMonitor.create(this.$el.querySelector('.signal'));

        elementWatcher.enterViewport(() => {
          this.visible = true;
        });
        elementWatcher.exitViewport(() => {
          this.visible = false;
        });
      }
    }
  }
});
.widget-container {
  height: 200px;
  margin: 10px;
  background-color: #f0f0f0;
  display: flex;
  flex-flow: row wrap; 
}

.signal {
  height: 10px;
  width: 10px;
  margin: 30px;
  background-color: red;
  border: thin solid blue;
}

.complex-stuff {
  flex-basis: 100%;
  padding: 15px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.2.6/vue.min.js"></script>
<script src="https://rawgit.com/stutrek/scrollMonitor/master/scrollMonitor.js"></script>

<template id="widget-template">
  <div class="widget-container">
    <div class="signal">
    </div>
    <div v-if="visible" class="complex-stuff">
      This is widget {{id}}.
      Blah blah blah.
    </div>
  </div>
</template>

<div id="app">
  Widgets below:
  <widget v-for="id in ids" :id="id"></widget>
  :widgets above
</div>
like image 132
Roy J Avatar answered Feb 07 '23 06:02

Roy J