Vue v-for performance is bad

I have about 4000 objects returned via AJAX. I iterate over them with v-for and spit them out into a table.

Initial loading and rendering is very fast, but I also have an input field that I use for "instant search". I'm using a computed property to filter a dataset using an input value and a small dataset, say up to 100 results, this works great, but as the dataset grows, it gets much slower.

I present a table with 4 values, one of which is a custom component. Removing the component makes things faster, but I'm surprised it's a bad result. I am not sure if there is something I am missing or can someone point me in the right direction?

I know its a lot of data for one page, but I thought this was what Vue was supposed to be for. I searched for this issue and for example found this codepen displaying a similar list of items and filtering exactly the same and I could copy - drop the number of items in the array to 10,000 or so and there was no noticeable performance improvement when searching.

The steps I took to speed things up made either tiny or no improvements:

  • Added v-bind key: with unique value for v-for
  • Don't use table element and use div or ul instead
  • Ditch the nativeJS.filter method as it can be slow and use my own filter method.
  • Trying to run it on a new codebase with only the dependencies it needs to run.
  • And I know about pagination techniques and the like, but I don’t wish to do this unless I have exhausted all other possibilities.

thank

He wants me to paste the code here even though I'm linked to the codepen, so here's JS without an array of elements.

    Vue.component('my-component', {
  template: '#generic-picker',
  props:['items','query','selected'],
  created: function(){
    this.query='';
    this.selected='';
  },
  computed:{
    filteredItems: function () {
      var query = this.query;
      return this.items.filter(function (item) {
        return item.toLowerCase().indexOf(query.toLowerCase()) !== -1})
    }
  },
  methods:{
    select:function(selection){
      this.selected = selection;
    }
  }
})
// create a root instance
var genericpicker = new Vue({
  el: '#example'
});

      

+3


source to share


1 answer


The problem with using a computed array is that things have to be unmapped and re-mapped as if you were using v-if

when you are in a situation where it v-show

is the best choice.

Instead, save an indicator for each item to show up and use v-show

based on that. The snippet below implements both options, which can be selected by the checkbox. You will find that the filter updates stall a little if you are not using the version v-show

, but handle v-show

well enough when using .

Most noticeably when you filter it down to 0 rows (say filter on x) then show everything (remove filter) but you can see the difference in partial filtering like me 2



let arr = [];
for (let i=0; i<6000; ++i) {
  arr.push({name: `Name ${i}`, thingy: `Thingy ${i}`});
}

Vue.component('tableRow', {
      template: '<tr><td>{{name}}</td><td>{{thingy}}</td></tr>',
      props: ['name', 'thingy']
    }
);

new Vue({
  el: '#app',
  data: {
    arr,
    filter: 'x',
    useVshow: false
  },
  computed: {
    filteredArr() {
      return this.filter ? this.arr.filter((item) => item.name.indexOf(this.filter) > -1) : this.arr;
    }
  },
  watch: {
    filter() {
      for (const i of this.arr) {
        i.show = this.filter ? i.name.indexOf(this.filter) > -1 : true;
      }
    }
  }
});
      

<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.3.3/vue.min.js"></script>
<div id="app">
  Filter: <input v-model="filter">
  Use v-show: <input type="checkbox" v-model="useVshow">
  <table>
    <tr>
    <th>Name</th>
    <th>Thingy</th>
    </tr>
    <template v-if="useVshow">
    <tr is="tableRow" v-for="row in arr" v-show="row.show" :key="row.name" :name="row.name" :thingy="row.thingy"></tr>
    </template>
    <template v-else>
    <tr is="tableRow" v-for="row in filteredArr" v-show="row.show" :key="row.name" :name="row.name" :thingy="row.thingy"></tr>
    </template>
  </table>
</div>
      

Run codeHide result


+3


source







All Articles