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:
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'
});
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With