Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Transitions with v-if statements at root-level of components aren't working the same way as in Vue 2 #7649

Closed
lehni opened this issue Feb 4, 2023 · 11 comments · Fixed by #7678

Comments

@lehni
Copy link
Contributor

lehni commented Feb 4, 2023

Vue version

3.2.45

Link to minimal reproduction

https://stackblitz.com/edit/vue-3-transition-with-root-level-v-if

Steps to reproduce

Compare with the same reproduction for Vue 2:

The reproduction displays a button that each time it is clicked, it cycles a counter between 1..2..3.
For each number, a component is rendered. One, Two and Three.
Component Two has a v-if="false" statement at root-level, essentially not rendering the component at all.

What is expected?

In Vue 2, once the counter reaches 3, component Three is rendered with a transition as expected, just like when the v-if is applied outside of component Two when it is rendered, e.g. with a chainged v-if, v-else-if statement nested in the Transition.

Screen-cast of the expected behavior:

vue-2-transition-with-root-level-v-if.mp4

What is actually happening?

In Vue 3, once the counter reaches 3, component Three is not rendered at all, the transition appears to be remaining in a confused state. Only once the counter goes back to 1, the transition starts working again.

Screen-cast of the actual behavior:

vue-3-transition-with-root-level-v-if.mp4

System Info

System:
    OS: macOS 13.1
    CPU: (8) x64 Intel(R) Core(TM) i7-1068NG7 CPU @ 2.30GHz
    Memory: 2.57 GB / 32.00 GB
    Shell: 5.8.1 - /bin/zsh
  Binaries:
    Node: 19.6.0 - ~/.nvm/versions/node/v19.6.0/bin/node
    Yarn: 3.3.1 - ~/.nvm/versions/node/v19.6.0/bin/yarn
    npm: 9.4.0 - ~/.nvm/versions/node/v19.6.0/bin/npm
  Browsers:
    Chrome: 109.0.5414.119
    Chrome Canary: 112.0.5578.0
    Firefox: 109.0.1
    Firefox Nightly: 111.0a1
    Safari: 16.2
    Safari Technology Preview: 16.4

Any additional comments?

While this particular example may seem a bit nonsensical, this situation can be encountered in a real scenario, e.g. if the component rendered uses v-if at root-level to only render its contents once some data is fetched asynchronously. In Vue 2, such a component would still apply a transition once it is ready to be rendered. This is broken in Vue 3 not only for the component itself, but even for the next component that would render itself after the one that uses v-if in such a way.

@AlexVagrant
Copy link
Contributor

This isn't a vue3 bug. if you use out-in mode in transition, you need to use <Suspense> tag to wrap your tag.

<template>
  <button @click="handleAddCounter">Step</button>
  to cycle through counter: {{ counter }} name is {{ names[counter] }}
  <div class="transition">
    <Transition name="slide-up" mode="out-in">
      <Suspense>
        <component :is="names[counter]" :key="counter"></component>
      </Suspense>
    </Transition>
  </div>
</template>

I find this issue resolution from https://github.com/vuejs/core/blob/main/packages/vue/__tests__/e2e/Transition.spec.ts#L1375

@lehni
Copy link
Contributor Author

lehni commented Feb 7, 2023

Thank you @AlexVagrant for pointing this out, but I am not sure it's save to use <Supsense>? From the docs:

<Suspense> is an experimental feature. It is not guaranteed to reach stable status and the API may change before it does.

https://vuejs.org/guide/built-ins/suspense.html

And if this is indeed the solution, then it should be mentioned in the Migration Guide

@lehni
Copy link
Contributor Author

lehni commented Feb 7, 2023

Follow up: It does indeed solve this problem (Update: that's sadly not the case, see below), but note that the docs only mention async dependencies, not this particular use-case. It's definitely not something one would naturally expect to solve this problem:

https://stackblitz.com/edit/vue-3-transition-with-root-level-v-if-and-suspsense

@lehni
Copy link
Contributor Author

lehni commented Feb 7, 2023

Oh, and when using this feature, you end up with such messages in the console:

<Suspense> is an experimental feature and its API will likely change.

@lehni
Copy link
Contributor Author

lehni commented Feb 7, 2023

While this does work for the reduced test-case I posted above, in the code base that I am porting from Vue 2 to Vue 3, <Suspense> does not actually replace the old functionality correctly. I end up with lots of these errors:

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 
  at <Transition mode="out-in" onBeforeLeave=fn<onBeforeLeave>  ... > 
Uncaught (in promise) DOMException: Failed to execute 'insertBefore' on 'Node': The node before which the new node is to be inserted is not a child of this node.
    at insert (http://localhost:3000/_nuxt/node_modules/.vite/deps/chunk-3NMN3MUW.js?v=cc5bdc30:7943:12)
    at move (http://localhost:3000/_nuxt/node_modules/.vite/deps/chunk-3NMN3MUW.js?v=cc5bdc30:6459:7)
    at move (http://localhost:3000/_nuxt/node_modules/.vite/deps/chunk-3NMN3MUW.js?v=cc5bdc30:6414:7)
    at activeBranch.transition.afterLeave (http://localhost:3000/_nuxt/node_modules/.vite/deps/chunk-3NMN3MUW.js?v=cc5bdc30:2576:15)
    at performRemove (http://localhost:3000/_nuxt/node_modules/.vite/deps/chunk-3NMN3MUW.js?v=cc5bdc30:6529:20)
    at remove2 (http://localhost:3000/_nuxt/node_modules/.vite/deps/chunk-3NMN3MUW.js?v=cc5bdc30:6541:7)
    at unmount (http://localhost:3000/_nuxt/node_modules/.vite/deps/chunk-3NMN3MUW.js?v=cc5bdc30:6496:9)
    at Object.resolve (http://localhost:3000/_nuxt/node_modules/.vite/deps/chunk-3NMN3MUW.js?v=cc5bdc30:2589:11)
    at patchSuspense (http://localhost:3000/_nuxt/node_modules/.vite/deps/chunk-3NMN3MUW.js?v=cc5bdc30:2515:18)
    at Object.process (http://localhost:3000/_nuxt/node_modules/.vite/deps/chunk-3NMN3MUW.js?v=cc5bdc30:2399:7)
insert @ runtime-dom.esm-bundler.js:10
move @ runtime-core.esm-bundler.js:6094
move @ runtime-core.esm-bundler.js:6043
activeBranch.transition.afterLeave @ runtime-core.esm-bundler.js:1394
performRemove @ runtime-core.esm-bundler.js:6184
remove2 @ runtime-core.esm-bundler.js:6200
unmount @ runtime-core.esm-bundler.js:6142
resolve @ runtime-core.esm-bundler.js:1405
patchSuspense @ runtime-core.esm-bundler.js:1326
process @ runtime-core.esm-bundler.js:1204
patch @ runtime-core.esm-bundler.js:5125
componentUpdateFn @ runtime-core.esm-bundler.js:5729
run @ reactivity.esm-bundler.js:190
instance.update @ runtime-core.esm-bundler.js:5763
callWithErrorHandling @ runtime-core.esm-bundler.js:173
flushJobs @ runtime-core.esm-bundler.js:406
Promise.then (async)
queueFlush @ runtime-core.esm-bundler.js:298
queueJob @ runtime-core.esm-bundler.js:292
(anonymous) @ runtime-core.esm-bundler.js:5761
triggerEffect @ reactivity.esm-bundler.js:400
triggerEffects @ reactivity.esm-bundler.js:390
trigger @ reactivity.esm-bundler.js:358
set2 @ reactivity.esm-bundler.js:543
set @ runtime-core.esm-bundler.js:3220
onIntersection @ DeferredRenderingMixin.js:101
handle @ intersection.js:83

@lehni
Copy link
Contributor Author

lehni commented Feb 7, 2023

Ok, click the Step button repeatedly at high frequency in the new version of the reduced test-case that include <Suspense> here, and you'll get the same errors. This definitely doesn't fix this bug:

https://stackblitz.com/edit/vue-3-transition-with-root-level-v-if-and-suspsense

image

@AlexVagrant
Copy link
Contributor

Hi guy, I know how to solve this bug.

It's a Transition bug, when we use v-if in Transition and the v-if result is false, Vue will insert a comment node to replace origin component, it is a placeholder node.

Transition get all children from slots, but the <!--v-if--> comment can't get from slots, this is the reason.

The following is a simple solution (not the final one):

https://github.com/AlexVagrant/core/blob/fix/transition_v-if/packages/runtime-core/src/components/BaseTransition.ts#L227-L228

@lehni
Copy link
Contributor Author

lehni commented Feb 9, 2023

@AlexVagrant great find! Are you working on a PR to fix this?

@AlexVagrant
Copy link
Contributor

@AlexVagrant great find! Are you working on a PR to fix this?

Yes, I will fix this bug soon, please give me a little time.

@lehni
Copy link
Contributor Author

lehni commented Sep 7, 2023

@yyx990803 would be great to see this merged, so we can finally remove the workarounds that we needed to add when transitioning from vue2 to vue3…

@lehni
Copy link
Contributor Author

lehni commented Jun 7, 2024

Thank you @yyx990803 and @AlexVagrant ! Can't wait to finally remove our workarounds for this : )

@github-actions github-actions bot locked and limited conversation to collaborators Jun 24, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
3 participants