vuejs / vue

This is the repo for Vue 2. For Vue 3, go to https://github.com/vuejs/core
http://v2.vuejs.org
MIT License
207.99k stars 33.69k forks source link

O(n^2) performance when tearing down deps #10435

Open ascott18 opened 5 years ago

ascott18 commented 5 years ago

Version

2.6.10

Reproduction link

https://jsfiddle.net/b9at5703/

Steps to reproduce

Click the button to toggle the render of the children on and off. Adjust the number of children rendered in the template.

Increasing the number of children by 10x (from 10,000 to 100,000) increases the render time by 10x, but increases the teardown time by 33x (from ~0.6s to 20s in my testing). I realize that 100,000 components is a pretty unrealistic use case, but please continue reading below.

What is expected?

Teardown time for components should scale linearly with the number of components, not exponentially.

What is actually happening?

In the repro link, every component has its own subscription against $root.theme. This is fine on its own, but subs are stored in an array, requiring an O(n) search to find the sub in order to remove it from the array.

This particular example is actually an extremely optimistic case - the subs are added to the array in order that the components are rendered, and then the components are destroyed in the order they are rendered. This means that the sub being removed is always found at the very beginning of the subs array, so the indexOf call in remove() (https://github.com/vuejs/vue/blob/dev/src/core/observer/dep.js#L28) can return almost immediately.

This is why I had to go to really high numbers of components (10k-100k) to make the problem very evident, but in my real-world application, I'm seeing this issue at 1,000 components or less (in a large data table component).

Now, imagine a scenario where a complex application has a large component tree with subs to some root theme prop all over the place. In fact, this is exactly what happens in a Vuetify app. As components are destroyed, their sub to this root value could be anywhere in the subs array.


Ran into this while using Vuetify, which has a subscription to $vuetify.isDark (saw 1700 subs), or to $vuetify.theme.dark (saw 1000 subs) in a large number of its components. I also saw 300 subs to $route, which could easily balloon much bigger if I added some router-links to some of the fields in my table cells.

ascott18 commented 5 years ago

There seems to be a PR for this (https://github.com/vuejs/vue/pull/8157, which appears to have languished), but I wasn't able to find a related issue.

trickstival commented 5 years ago

It's probably going to be a part of Vue 3.x as stated in https://github.com/vuejs/vuex/issues/1383#issuecomment-429617484