Subscribe on changes!

SSR hydration throws error when slot (fragment) contains a single `<transition>` whose element's `v-if` evaluates to `false`

avatar
Feb 1st 2022

Version

3.2.29

Reproduction link

https://github.com/AaronBeaudoin/vite-vue-ssr-bug-repr/tree/transition-mismatch

Steps to reproduce

  1. npm i to install dependencies then npm run dev to run.
  2. 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.

avatar
Feb 2nd 2022

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:

  1. parentVNode (the fragment) has 0 children, so therefore node is the fragment's end anchor, causing the for loop to never run and the end anchor to be returned directly.
  2. parentNode (the fragment) has 1 or more children, so therefore node is the fragment's first child, causing the for loop to run such that on the first iteration the hydrateNode function is called with node and parentVNode.children[0]. As the loop runs parentVNode.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.

avatar
May 12th 2022

Might be duplicate of #3989 ?