SSR hydration throws error when slot (fragment) contains a single `<transition>` whose element's `v-if` evaluates to `false`
Version
3.2.29
Reproduction link
https://github.com/AaronBeaudoin/vite-vue-ssr-bug-repr/tree/transition-mismatch
Steps to reproduce
npm i
to install dependencies thennpm run dev
to run.- In your browser go to
http://localhost:3001
. Note the error in the console.
What is expected?
Hydration completes without errors.
What is actually happening?
Hydration throws an error.
SEE THE "More Debugging Info" COMMENT BELOW FOR AN EXPLANATION.
More Debugging Info
Here is more specifically what is happening with this bug. When the hydrateChildren
function is run inside of the hydrateFragment
function on line 4283, it expects to get the fragment's end anchor back from hydrateChildren
, but in this case it get's null
instead.
The reason for this can be seen inside of hydrateChildren
. It appears that there are two expected states of node
and parentVNode
inside this function:
parentVNode
(the fragment) has 0 children, so thereforenode
is the fragment's end anchor, causing the for loop to never run and the end anchor to be returned directly.parentNode
(the fragment) has 1 or more children, so thereforenode
is the fragment's first child, causing the for loop to run such that on the first iteration thehydrateNode
function is called withnode
andparentVNode.children[0]
. As the loop runsparentVNode.children.length
times,node
is overwritten on each iteration, eventually resulting in the function returning the fragment's end anchor.
Either way, parentVNode
is the fragment, and the function's return value is expected to be the end anchor.
In the minimal reproduction above, neither of these things happen, which appears to undefined behavior and causes a bug. In this case, when the hydrateChildren
function runs, parentVNode
(the fragment) has 1 child (the <transition>
), but node
is the fragment's end anchor because the transition's element does not render (since its v-if
evaluates to false
). This results in the hydrateNode
function being called with the fragment's end anchor as node
but the transition as vnode
, which clearly doesn't make sense.
It appears that this state in turn causes the hydrateNode
function which runs on the for loop's only iteration to return null
, which is then returned from hydrateChildren
causing the hydrateFragment
function to never get the fragment's end anchor—even though it exists in the server rendered HTML!
This then results in the hydrateFragment
function inserting a new comment, which occurs on line 4292. This results in the DOM having 3 comments like:
<div id="app">
<div>
<!--[-->
<!--]-->
<!--]-->
</div>
</div>
...and now there is an extra unmatched fragment end anchor in the DOM. So this bug results in a bit of a mess.