`watch()` does not work after building for production using `npm run build`
Vue version
3.2.45
Link to minimal reproduction
will add zip file to this issue
Steps to reproduce
- Run
npm run dev
- Observe that watch is called
- Run
npm run build
and serve usingnpx http-server dist
- Observe that watch is not called
What is expected?
watch callback gets called
What is actually happening?
watch callback does not get called
System Info
No response
Any additional comments?
creating this issue to bring attention back to (https://github.com/vuejs/core/issues/5707)
looks like the bug affects both watch()
and watchEffect()
Creating the minimal reproduction, I've learned that it has something to do with:
<Suspense>
inApp.vue
- The use of
await
inHomeView.vue
- Replacing
await
withvoid
inHomeView.vue
makes the problem go away. <Suspense>
was added inApp.vue
because without it, the use ofawait
inHomeView.vue
gives errors.
This seems to be unrelated to #5707.
The problem here is that the component's props
object is not being properly updated when the route changes - so the watch of course can't run, as props
didn't change.
Here's what I could find out quickly:
The renderer triggers a re-render of the HomeView component, because in the component's vnode, the prop has been updated. However the props
object in the component itself has not been updated with that new value, and so the watch is never triggered.
Addign this to HomeView
:
onBeforeUpdate(() => {
const vnodeProps = getCurrentInstance()?.vnode.props
console.log('updating...', { ...props }, { ...vnodeProps })
})
results in this console entry:
So, seems to be a problem with Suspense, maybe in combination with <RouterView>
.
/cc @posva does this look familiar to some problem you have seen before?
patchFlag
is 0
, so it should receive a full props diff, but optimized
is true
, so line 3823 evaluates to true, so the code enters that branch - where it then does nothing because patchFlag is 0
(see line 3825).
That only happens when HomeView
is async. When that component is not async, optimized
is false
, and thus a full props patch is being done, so the app works as expected in that case.
There are other bugs with Suspense and keep alive that appear because the entering component renders before the leaving component. There are some open issues in vue router about this. But I don't recall any problem with a different behavior between dev and prod
Got it. when calling suspense.registerDep
, we don't pass the current value of optimize
. Instead, in registerDep
, we use the closure's value of optimized
that was passed to createSusenseBoundary()
I think we would need to change registerDep
to accept the optimized
flag as a third argument?
registerDep(instance, setupRenderEffect, optimized = false) {
Not sure how to write a proper test for this though.
The difference of behavior between dev and production is caused by the optimized
parameter? @LinusBorg