vue
vue copied to clipboard
list rendering optimization fails in some cases
I have virtual scrolling list with a lot of rows. I only show a few of them at a time using
this.view = this.items.splice(position, position + 5)
If 'position' is incremented or decremented, you would expect that in the DOM one element gets added, and one gets deleted. In the 'up' direction, that is the case. But in the down direction, all elements get removed and added again. I have a couple of images in each row, and this hurts performance quite a bit.
Vue.js version
2.1.3
Reproduction Link
https://jsbin.com/netereyuxa/1/edit?html,console,output
Steps to reproduce
press up / down, see that in the up direction one LI is removed and one added at each step, and in the down direction all LIs are replaced every time.
What is Expected?
Only one LI added, one LI removed
What is actually happening?
All LI's removed and added
To be specific:
- the
<li>
s are being properly reused (i.e. not replaced by newly-created elements) - there are indeed more "move" operations than necessary.
However, based on Chrome's timeline tracing, it seems both moving up and down has very similar rendering/painting costs. I wouldn't really consider this "failing", but maybe it can be improved. Are you experiencing visible slow down in your app? How complex is each item in your actual list?
On a reasonably modern laptop you can't see the difference. I think can see a slight difference on my Asus TF300T tablet, but that one is four years old.
The use case is a virtual scroller. It shows a couple of rows in the viewport, and when scrolling rows are inserted / deleted as they scroll into / out of view. Each row is a LI element, which contains 5 DIVS, each with an IMG (20K jpeg) and some text.
I was just trying to optimize the list rendering in my component, noticed this issue, and thought this was a small bug in the algorithm, that's why I reported it. If it's hard to fix correctly, or impossible to fix without some other performance impact, then please don't bother :)
Thanks for the quick reply!
Mike.
I am also implementing a virtual scroll feature and I came across the exact same problem. Unfortunately the problem seems to hurt performance quite a lot when smooth scrolling, e.g. on a Macbook Pro with the touchpad. Scrolling upwards is buttery smooth, while scrolling downwards creates micro-stutter caused by the many dom updates. Here's a capture from #7477 that shows the problem very well (https://jsfiddle.net/1m5vx6dc/1/):
I just tested this with Vue 3 - with the example provided by mofux above. It looks like this is fixed now, so this issue can perhaps be closed.
If you're using Vue version 2: I found a dirty fix that seems to work for me. I had a input control inside a list item that lost focus when the current list item moved down, but not up. This problem can be (kind of) avoided when adding a watch combined with a nextTick that checks if the control has lost focus. I don't know that this works 100% in every case, but this is how I fixed my issue:
items: {
handler(newValue) {
const focusedElement = document.activeElement;
console.log(focusedElement);
this.$nextTick(function () {
if (document.activeElement !== focusedElement) {
focusedElement.focus();
}
});
},
deep: true,
}
Here is an example: https://codepen.io/and3k5/pen/zYewLxN