Relatively poor performance when prepending to a large array
Version
3.2.24
Reproduction link
Steps to reproduce
- Click on the "add 50" button
What is expected?
The action should be reflected immediately
What is actually happening?
The action's result is delayed, and we can observe "Jank - event processing delay" when capturing the performance with Firefox
A similar problem can be observed with splice
if the insertion point is close to the beginning of the array. But if we replace unshift
with push
, there does not seem to be any performance penalty; this seems to point towards something that is run for every existing element in the array.
This problem was not existing in the latest vue2, cf: https://codesandbox.io/s/mystifying-monad-8n0h4?file=/src/App.vue
Might be related to: https://github.com/vuejs/vue-next/issues/4318
Let's keep one issue with https://github.com/vuejs/vue-next/issues/4318
In your case, you can use a few tricks like Object.freeze()
or markRaw()
to improve performance. There are a few other tricks in the docs too
Just as a preliminary explanation:
- calling
push()
on an array with 2000 items results in 1 set operation. - calling
unshift()
on an array with 2000 items results in 2001 set operations, as all array items have to be reassigned to index+1.
The latter is still very fast with plain arrays as the Browser JS engine likely has a highly optimized codepath for this. But when a reactivity proxy is involved, that optimization is lost.
note that calling unshift in a loop, invokes reactivity overhead per each call. the lost of optimization mentioned is actually the overhead of the reactivity system implementation.
Adding 50 items to a 2000 items array will iterate the full list 50 times performing different check to maintain the reactivity system ( ~2000 x 50 )
a possible user land optimization is for you to batch the data to a single prepend call.
SFC playground- slow prepend on large list , get faster with batching (10x faster)
can vue batch all prepend mutation calls within the "tick" and invoke logic as if it was an atomic operation, that I do not know.
If you do not intend to mutate the items in the list (e.g. they are server-fetched data), another way to improve performance with large lists is going immutable - mark the list with markRaw
to prevent Vue from making it reactive, and trigger list updates by re-assigning the list: example