Unrelated prop watcher is called when updating prop value.
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:
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
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.
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.
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 templatethe 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.