Subscribe on changes!

Unrelated prop watcher is called when updating prop value.

avatar
Jul 12th 2023

Vue version

3.3.4

Link to minimal reproduction

https://github.com/LittleSaya/vue3-issue-demo

Steps to reproduce

git clone https://github.com/LittleSaya/vue3-issue-demo
cd vue3-issue-demo
npm install
npm run dev

then open the dev site, click 'Fn', two alerts appear.

What is expected?

Only one alert (the 'dbg watch b') is expected to pop up, as only prop b is modified.

What is actually happening?

Both alerts (the 'dbg watch b' and 'dbg watch a') poped up.

System Info

System:
    OS: Windows 10 10.0.19045
    CPU: (12) x64 Intel(R) Core(TM) i7-8750H CPU @ 2.20GHz 
    Memory: 5.21 GB / 15.88 GB
  Binaries:
    Node: 16.15.1 - D:\Program Files\nodejs\node.EXE       
    Yarn: 1.22.4 - D:\Program Files (x86)\Yarn\bin\yarn.CMD
    npm: 8.11.0 - D:\Program Files\nodejs\npm.CMD
    pnpm: 8.5.1 - ~\AppData\Roaming\npm\pnpm.CMD
  Browsers:
    Edge: Spartan (44.19041.1266.0), Chromium (114.0.1823.67)
    Internet Explorer: 11.0.19041.1566
  npmPackages:
    vue: ^3.3.4 => 3.3.4

Any additional comments?

There are two *.vue files, the modification is done in App.vue, watchers are in Comp.vue.

There are three props in the demo, two arrays and one number, the number (prop c) looks ok, its watcher is not called when prop b is modified, but prop a's watcher is called when I modify prop b.

I know it may look weird that I'm listening to a prop populated with literal value, but the demo repo is a minimized version of a full product just used to demostrate this problem, and in the real product I need that component to be able to receive both static value (literal values, for example) and dynamic value. I have watchers on both props, both have side effects, so I definitely not want any of them been called out of expectation.

This problem only occurs in development environment, it works well when I build it and visit it through nginx.

I tried vue-cli, but the problem is still there, so I think it is not caused by build tool.

I failed to reproduce this problem in Vue SFC Playground, which confused me, because I think the playground is some kind of development environment, this is what I have tried in the playground:

https://play.vuejs.org/#eNqtU02P2jAQ/SsjX2ClKKilJxqifohDe2hXbW+EgxMmIbuObdkOICH+e8d2Auxq+3HYS5KZ92b8PPNyYh+1Tvc9sgXLHHZacId5IQGyz6rTsODLgq3fJPB2UzBYlBSV9DGLlLJ3Tkn4UIm2eiSolgXLa5nNIkCkbHbTlEJbmVY7EFw2xHeWell0vSa07bQyDk5gsIYz1EZ1MCFlk/cXLEiKQDrzgVfu8UJWSloHJSx9eSb7rkSz3uTT9eYu4HUvK9eS2FpO7+Dk5ZfpnoseqWQ9T+DdhnhnrzhqJEUsIYXUuG6b9MEqSTMKhQWr6OxWoPmufU+6xSK29BgXQh2+hpwzPSZjvtph9fhC/sEefa5g9wYtmj0W7II5bhp0EV79/IZH+r6Andr2gth/AX+gVaL3GiPtUy+3JPuGF9R+CeNtZfPLro4OpR0v5YV65jnwC0bj9mP/09WvcufpPNTRSGmK46qemswED1nNZS6V29HxsEODtACf8vCteygMqcFBwTW3PgqMi4cO3FW7Zy4aOkSnaKO0pdVvsW4l3vsoO0VJfAGjf0IROeVZ5pxPyVVDv3DUNBLJWss89k55cpsbegPE49EYOlziAVbGKDO0G3ElMBWqmU62ZTNchfuSSeKfVy6nPbgnrMkI0sbo/W+V5auqLP9LZfmiyuuPRwE7/wYqjYUx

Updates

Another two computers fall into this problem:

  System:
    OS: Windows 10 10.0.19042
    CPU: (20) x64 Intel(R) Core(TM) i9-10900K CPU @ 3.70GHz
    Memory: 22.31 GB / 31.92 GB
  Binaries:
    Node: 16.15.1 - D:\Program Files\nodejs\node.EXE
    npm: 8.11.0 - D:\Program Files\nodejs\npm.CMD
  Browsers:
    Edge: Spartan (44.19041.423.0), Chromium (114.0.1823.67)
    Internet Explorer: 11.0.19041.1
  npmPackages:
    vue: ^3.3.4 => 3.3.4
  System:
    OS: Windows 10 10.0.22621
    CPU: (16) x64 12th Gen Intel(R) Core(TM) i5-1240P
    Memory: 8.03 GB / 15.69 GB
  Binaries:
    Node: 18.16.1 - C:\Program Files\nodejs\node.EXE
    npm: 9.5.1 - C:\Program Files\nodejs\npm.CMD
  Browsers:
    Edge: Spartan (44.22621.1992.0), Chromium (114.0.1823.67)
    Internet Explorer: 11.0.22621.1
  npmPackages:
    vue: ^3.3.4 => 3.3.4
avatar
Jul 12th 2023

Can't reproduce it.

avatar
Jul 12th 2023

When you change one of the props, App.vue re-renders. In that re-rendering, you create a fresh array in the template each time and pass that fresh array to prop a. Hence why the watcher for the a prop gets called each time - its a different array each time.

avatar
Jul 12th 2023

And here's why it only happens in dev, if I recall correctly:

In prod, we do a finer-grained props update - we do a looseEqual check to catch precisely the case I described before: elements created in the template the compiler knows which props are stable (like your static inline array in :a="") and which can potentially change. Only those will actually be sent to the child during an update.

However in dev, due to requirements from hot reloading (and as such, only in dev with HMR enabled), we send all props to the child, regardless of wether or not they are stable, because you might have changed the "stable" value in a hot reloaded edit of the template.

avatar
Jul 13th 2023

And here's why it only happens in dev, if I recall correctly:

In prod, we do a finer-grained props update - we do a looseEqual check to catch precisely the case I described before: elements created in the template the compiler knows which props are stable (like your static inline array in :a="") and which can potentially change. Only those will actually be sent to the child during an update.

However in dev, due to requirements from hot reloading (and as such, only in dev with HMR enabled), we send all props to the child, regardless of wether or not they are stable, because you might have changed the "stable" value in a hot reloaded edit of the template.

@LinusBorg Thank you for your reply! I turned off hmr and the problem is gone, hope this inconsistency will be fixed in future versions.