Watchers vs. "unchanged" arrays

vue-core
watchers

#1

I was wondering whether watchers being triggered when replacing an array with a different instance of an array with identical contents is a bug or a feature in Vue. I understand the underlying comparator probably uses === but the $watch documentation is not 100% clear on what makes a “change”. The fact that there is also “deep” watching but it does not apply to arrays does not make the situation better.

  • Is this actually a bug or a feature?
  • If it is a feature, is it somewhere documented?
  • If it is a feature, how do people go about not triggering watchers when arrays are replaced with identical contents (other than using your own “deep equals” algorithm in the watcher callback)?

Here is a fiddle I used for playing around: https://jsfiddle.net/8v0g9zou/1/


#2

It’s not a bug. The watcher watch the change of the property’s value. A new array is a new object, and hence, the property’s value has changed, and the watcher fires. We don’t ever do any “deep” comparisons anywhere (which would be performance sinkholes). so It seems that would be expected behaviour imho.

Indeed.

It’s not documented in any special way because this is not a “feature”, it’s the way that javascript works: Two arrays with identical contents are still two distinct objects, so the property’s value “changed” by any meaningful definition of that word in the context of Javascript.

Javascript doesn’t have a built-in way to do a “deep equality” check - unless you count writing some sort of custom comparison algorthm for this. So a deep comparison would be something unusual, and worth documenting. A shallow comparison would not.

a. Don’t replace the array. replace its contents.
b. Do the deep comparison in your watcher’s handler on your own and only proceed if the deep comparison is failing.

Out of curiosity, what’s the usecase? Never needed to do this, really.


#3

Hey @LinusBorg, thanks for your reply!

I am very well aware of that. What tricked me into believing that Vue might have some sort of deep comparison was my incomplete understanding of what the deep option of vm.$watch was.

It’s an audio application where the Vue state represents the desired state of the audio engine (what should play when and how). The Web Audio graph state is updated using watchers (as the audio engine state has nothing to do with the DOM). Unlike when updating DOM structures from arrays using data binding, “no-op” updates to arrays do cause “changes” when using watchers. The same behavior applies to any kind of Object, not only Arrays btw. But now I understand better.

What my use case would probably need is some equivalent of the virtual DOM that makes the least amounts of changes to the audio engine state whenever a Vue state property changes. A quick and dirty version of this will be using “deep equal” comparison.

I think this has nothing to do with my case. No matter if I replace the array with a new array of identical contents or replace the contents of the array “in-place” with identical contents (eg. using splice) I get the same behavior. The Vue Guide also does not particularly recommend against array replacement: https://vuejs.org/v2/guide/list.html#Replacing-an-Array


#4

Yeah you’re right. That approach won’t really work.