最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Vue v-for performance is poor - Stack Overflow

programmeradmin3浏览0评论

I have about 4000 objects being returned via AJAX. I'm looping over them with v-for and spitting them out into a table.

The initial load and render is very fast but I've also got an input field I'm using for 'instant search'. I use a computed property to filter the dataset using the input value and on a small dataset, say up to about 100 results this works superbly but as the dataset gets larger it gets a lot slower.

I'm rendering a table with 4 values, one of which is a custom Component. Removing the Component speeds things up but I'm surprised that it's this bad a performance hit. I'm not sure if there's something I'm missing or if someone could point me in the right direction?

I know its a large amount of data for one page but I thought this was what Vue was supposed to be good at. I googled the issue and for instance I found this codepen rendering a similar list of items and filtering in exactly the same way and I could copy-paste the number of items in the array all the way up to 10,000 or so and there was no perceptible performance hit when searching.

Steps I've taken to speed things up, these have made either tiny or no improvements:

  • Added a v-bind:key with a unique value on the v-for
  • Not using the table element and instead using div or ul
  • Forgoing the nativeJS .filter method because it can be slow, and using my own filter method.
  • Trying running it on a fresh codebase with only the dependencies that are needed to run.
  • And I am aware of pagination techniques etc. but I'm unwilling to do that unless I've exhausted all other possibilities.

Thanks

It wants me to paste code here, even though I've linked to codepen so here's the JS without the items array.

    Vueponent('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'
});

I have about 4000 objects being returned via AJAX. I'm looping over them with v-for and spitting them out into a table.

The initial load and render is very fast but I've also got an input field I'm using for 'instant search'. I use a computed property to filter the dataset using the input value and on a small dataset, say up to about 100 results this works superbly but as the dataset gets larger it gets a lot slower.

I'm rendering a table with 4 values, one of which is a custom Component. Removing the Component speeds things up but I'm surprised that it's this bad a performance hit. I'm not sure if there's something I'm missing or if someone could point me in the right direction?

I know its a large amount of data for one page but I thought this was what Vue was supposed to be good at. I googled the issue and for instance I found this codepen rendering a similar list of items and filtering in exactly the same way and I could copy-paste the number of items in the array all the way up to 10,000 or so and there was no perceptible performance hit when searching.

Steps I've taken to speed things up, these have made either tiny or no improvements:

  • Added a v-bind:key with a unique value on the v-for
  • Not using the table element and instead using div or ul
  • Forgoing the nativeJS .filter method because it can be slow, and using my own filter method.
  • Trying running it on a fresh codebase with only the dependencies that are needed to run.
  • And I am aware of pagination techniques etc. but I'm unwilling to do that unless I've exhausted all other possibilities.

Thanks

It wants me to paste code here, even though I've linked to codepen so here's the JS without the items array.

    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'
});
Share Improve this question asked May 11, 2017 at 10:58 peospeos 831 gold badge1 silver badge5 bronze badges 5
  • 1 Add lazy modifier on v-model - <input type="text" v-model.lazy="query" /> – Belmin Bedak Commented May 11, 2017 at 11:00
  • @BelminBedak Doesn't help I'm afraid, I should point out I've also tried not using v-model and instead calling a method to update the filterText field and it didn't help either. I then tried to use lodash's .debounce on that and again no joy. Thanks anyway. – peos Commented May 11, 2017 at 11:06
  • Can you post complete example of your problem? here is my demo working fine with 40000 (not 4000) items... – donMateo Commented May 11, 2017 at 12:20
  • working link – donMateo Commented May 11, 2017 at 12:29
  • instantiating up to 4000 components can be constly, depending on how complex they are themselves - the filtering and DOM updates are not the problem. There are different optimization techniques - e.g. using functional components when possible, and/only rendering child components when they are visible in scrolling, or even only rendering table rows when they are visible in scrolling (github.com/Akryum/vue-virtual-scroller) – Linus Borg Commented May 11, 2017 at 13:31
Add a comment  | 

2 Answers 2

Reset to default 12

The problem with using a computed array is that things have to be un-rendered and re-rendered as if you're using v-if, when you're in a situation where v-show is a better choice.

Instead, keep an indicator for each item for whether it should be displayed, and use v-show based on that. The snippet below implements both, selectable by checkbox. You will find that filter updates are a bit halting when not using the v-show version, but keep up quite well when using v-show.

Most noticeable when you filter it down to 0 rows (say, filter on x) then show everything (remove the filter), but you can see a 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>

If you're not interested in two-way and/or reactive binding, that is, if you only want to visualize the objects rather than being able to edit them or update the view when the data changes, you can speed up performance dramatically with Object.freeze. This way, Vue.js can't add watchers on every property and instead only read the properties.

发布评论

评论列表(0)

  1. 暂无评论