Subscribe on changes!

Bad performance for deep watch on large structure

avatar
Jul 1st 2021

Version

3.1.2

Reproduction link

https://jsfiddle.net/41usdmr0/

Steps to reproduce

Run the jsfiddle while performing a performance analysis, press the button and stop the performance analysis.

What is expected?

The code basically just deep-watches a large object, which is modified by the button (the smaller part of the object). I expect the watch to be called, using just a tiny amount of computation, as the watch is not doing anything fancy.

What is actually happening?

The watch is called, as expected, but the processing around that is huge. In my case the browser is running JS Vue stuff for approximately 200 ms, which is at least unexpected, as it is not doing anything that would require such processing time. The large array within the object is not even used anywhere. The processing time increases when making the array even larger.


I am not sure if this is a bug or a feature request. I also asked about this in the forum over at https://forum.vuejs.org/t/bad-performance-for-watch-on-large-structure/118354 but got no answer, so this does not seem to be a well-known issue.

Edit: Here is a screenshot: vueWatchSlow

avatar
Jul 1st 2021

You are using a deep watcher, it's expensive. In prod it does take 70ms. If you don't need it (for object for examples), just use a different watcher that is more precise. You can also create multiple watchers

Vue.watch(
      () => [data.test, data.datalist],
      (newVal, oldVal) => {
        console.log('bla')
      }
)

I'm unsure if there is anything that can be done here

avatar
Jul 1st 2021

Thanks for the suggestion, but I need a watch on that complete large structure. It's for an undo/redo feature and this struct holds my complete state. Can you tell what Vue is actually doing in the background in that case?

avatar
Jul 1st 2021

That's expected - deep watches are by definition expensive because it traverses the entire data structure and collect every single property as dependencies for that watcher.

There's not much we can do here - since there is no magic deep watch natively in JS. Even with the pending improvements in 3.2, I don't think you'd want to ever use deep watch on such huge data structures.

If you are doing undo/redo, it'd be obviously expensive to traverse + serialize the entire state on every single interaction. You can skip the deep watch cost by instead explicitly saving the state on known interactions. Or better define your changes so you can just record the change patches and revert / replay them.

That said, undo/redo with large datasets is inherently a complex problem and it's not something deep watch is designed for. You may also want to try immutable data structures which are much more well suited for undo/redo than mutable systems (if you do, make sure to also use markRaw to prevent Vue from attempting to observe them).

avatar
Jul 2nd 2021

Thanks a lot for your elaboration and I can (have to?) accept when you say that you cannot do much about this. I'm completely new to Vue and JS, so I don't understand a lot of things. I know this is not a forum to answer usage-questions, but I would appreciate a small explanation why a watch is such expensive. I had a look into how Proxies work and as far as I understood you get a function called when a get/set is performed on that structure. All you need to do now is to set a flag "hey, this structure changed" or push something to a queue, which can be processed later on. Why do you need to traverse through the complete structure after the initialization? Yeah, the initialization may be costly, but that is not the problem here.