Fatal SSR Hydration crash
Vue version
3.2.44
Link to minimal reproduction
https://github.com/wucdbm/vue-hydration-bug
Steps to reproduce
yarn install
yarn codegen
Working fine
yarn dev
Visit http://127.0.0.1:3000/characters#characterId=9 Popup should be open
Not Working (dev ssr)
yarn ssr
Visit http://127.0.0.1:3000/characters#characterId=9 Popup is not open and console is full of errors
Not Working #2 (pkg build)
yarn build
yarn build:ssr
./dist/vue-test-bug
Visit http://127.0.0.1:3000/characters#characterId=9 Popup is not open and console is full of errors
What is expected?
It is expected that since we serialize the state via URQL's SSR middleware, and since the "query" param (yes, we do pass it via the URL's hash, for SEO reasons) is present, once it's open in the browser, it should quickly update its state and open itself asap, start "loading" the content (should finish ASAP due to URQL SSR cache) and present itself.
I also expect a hydration error since the server doesn't see the hash, that's OK. We will likely go around that with something like ClientOnly
or some sort of lazy loading on first browser idle.
I do NOT expect however for the app to fatally crash and the user not having any way to escape since navigation or any interaction with it whatsoever no longer works.
What is actually happening?
Instead, there are a bunch of hydration-related issues which is probably fine (we could use lazy-loading to circumvent that and load the popup on browser idle), but the problem is a runtime-core.esm-bundler.js:40 [Vue warn]: Unhandled error during execution of scheduler flush. This is likely a Vue internals bug. Please open an issue at https://new-issue.vuejs.org/?repo=vuejs/core after which nothing works at all and any following error is probably caused by this previous error that caused it to crash somehow.
System Info
System:
OS: Linux 5.15 Manjaro Linux
CPU: (20) x64 12th Gen Intel(R) Core(TM) i9-12900H
Memory: 54.11 GB / 62.47 GB
Container: Yes
Shell: 5.1.16 - /bin/bash
Binaries:
Node: 19.0.0 - /usr/local/bin/node
Yarn: 1.22.19 - /usr/local/bin/yarn
npm: 8.19.2 - /usr/local/bin/npm
Browsers:
Firefox: 105.0.3
npmPackages:
vue: 3.2.44 => 3.2.44
Any additional comments?
I didn't try using a render function to implement the generic components such as popup+card+slots (one of which is conditional) What I did notice though is that if we don't pass any props to the CharacterModal, it doesn't seem to want to check if it's rendered correctly (probably an optimization), hence no breakages, just displays the content coming from SSR - empty card modal in a hidden state.
Besides that, we do use a plugin of our own to build the project and run a dev ssr via vite, but I don't think it's causing any issues.
@yyx990803 Managed to narrow this down to instances where a <template>
has a v-if on it, in runtime-dom.esm-bundler.js
nodeOps.insert
parent
is NULL
The reason for using a is to avoid polluting the DOM with redundant DIVs
It seems that it can recover successfully from DIVs with v-ifs with SSR content mismatches, but not when it's a template. The template contains multiple children, sometimes something at the start that is always displayed and another chain of v-ifs
When I replace the template
with div
, but otherwise keep everything else as-is, it won't crash on the first template
, but instead keep going and list all the mismatches and re-render. With a template, it would crash hard on the first occurrence and ask me to file a bug report.
Could it be that this is an overlooked case, where because the template does not have its own representation in DOM, it tries to attach whatever should be there to the template which doesn't really exist in DOM and thus crashing hard?
Is it a sensible solution in such cases to replace the parent
argument passed with the template
tag's parent, since the template
itself won't render an element in DOM?!
Furthermore, sometimes it makse sense to conditionally pass a slot to a generic component - such as a visual "Card" - sometimes it could have a footer, other times not, depending on state, in which case I'd do <template #footer v-if="something">... content ... and in Card.vue conditionally render a footer container with the slot inside of that if there's any slot content passed in the first place - I am guessing that's what's causing the example repo to crash as described
Today's debugging I was debugging a similar crash some place else, where I was using a template tag to just group a bunch of elements/components to render under the same condition in one of the branches of a v-if-else-if-etc chain, but the root cause seems to be the same problem, as described.
Sadly, that's as far as my understanding of Vue internals goes, I hope this helps!