core: Transition not working with custom dynamic components

Version

3.0.0-alpha.10

Reproduction link

https://jsfiddle.net/posva/z9vseqgj/1/

Steps to reproduce

  • Click on the toggle button multiple times

What is expected?

  • The three elements to animate the same way
  • The second toggle should use a different animation for all elements

What is actually happening?

  • The 1st element, router-view:
    • seems to enter right away without leaving transition
    • only uses the fade transition (initial value for transitionName)

Using a key="component.name" attribute on router-view changes the behavior but does not fix it. It works on Vue 2 (with key): https://jsfiddle.net/posva/wsr78d2w/233/

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Comments: 22 (4 by maintainers)

Most upvoted comments

After a while of investitgation it a bit further, it turns out, that comments are messing the functionality up.

I’m not sure if this is happening in your case as well @dacxjo , but for me the issue was a comment before the first element within the template

<template>
  <main>
    <!-- Introduction -->
    <section class="mb-8 py-20 text-white text-center relative">
     <!-- rest of the code -->
     </section
  </main>
</template>

After removing the comment <!-- Introduction --> , everything worked as expected with this markup

 <router-view v-slot="{ Component }">
    <transition name="fade" mode="out-in">
      <component :is="Component"></component>
    </transition>
  </router-view>

Make sure that your other template files which you use as view/page components do not have a comment before the first element as well.

As it is mentioned in the offical docs of vue-router v4

Hope that helps in your case as well.

Cheers 😃

The css as per the docs is now incorrect for V3 transitions. So anyone who is coming here who has copied the fade css from the docs, here is the updated code:

  <router-view v-slot="{ Component }">
    <transition name="fade" mode="out-in">
      <component :is="Component"></component>
    </transition>
  </router-view>

Paste this over yours, even if it looks the same

.fade-enter-active,
.fade-leave-active {
  transition: opacity 0.35s ease;
}

.fade-enter-from,
.fade-leave-active {
  opacity: 0;
}

There are two parts in this repro:

  1. Dynamic transition names not updating on nested HOCs. This is fixed by b8da8b2d.

  2. Transition mode not working on nested HOCs’ own updates:

<transition mode="out-in">
  <router-view/>
</transition>

Note <router-view/> updates itself, not as a result of being toggled from outside.

I would say this is not a bug given the internal changes made in v3. The reason such usage in v2 worked is because:

  1. <router-view> in vue-router 3.x is a functional component;
  2. A Vue 2 functional component is a transparent call - it doesn’t have its own render cycle and directly returns the inner vnode it renders.

Therefore in the above use case, any reactive dependency registered during the render of <router-view/> is tracked by the parent component of <transition> (In v3 it’s tracked by <transition> because it’s in a slot). In either case when the dependency changes it would trigger <transition> to re-render, and that is what’s required for modes to work.

Now, in this repo:

  1. The <router-view> in vue-router@next is a stateful component
  2. But even if it’s a functional component, Vue 3 functional components are no longer transparent and they have their own reactivity collection, so it wouldn’t make any difference.

The <router-view> collects its own dependencies - so when the component ref is updated, only <router-view> itself re-renders - <transition> knows nothing about its internal change.

In addition, even if <transition> happens to update in the same tick, in its eyes its content did not change because it’s the same <router-view> vnode with no props. (In Vue 2 it would be different vnodes because it directly resolves to whatever the functional <router-view> returns). This is a more fundamental problem and in fact it prevents your current implementation of <router-view> to work with <keep-alive> in Vue 3 as well - because in the eyes of <keep-alive> it’s always the same component.

Fundamentally, this is about the behavior change of v3 functional components, from being transparent to having its own vnode and reactivity boundary. Overall v3 functional components behaves a lot more consistent to stateful ones and are more like just a syntactical alternative.


Now, we may need some design changes in the router to solve this use case. I think we should be able to support a slot on <router-view>:

<router-view #match="{ component, params }">
  <transition :name="transitionName" mode="out-in">
    <component :is="component" v-bind="params"></component>
  </transition>
</router-view>

This actually opens up some interesting flexibility as well (e.g. can decide how to pass params and queries to the matched component) and I think deserves an RFC.

Here’s a working demo: https://jsfiddle.net/yyx990803/7hsx4mz1/

I have the same issue as stuta. Same Pug template that swaps components. Works fine with no mode declared or with ‘in-out’ declared. If mode=“out-in” is declared, it returns nothing. I am not using Vite. Using vue-cli 4.5.0.

The three elements will perform the same way with #794 (with key)

Has there been any recent news regarding having comments at the top of the file breaking router transitions? Having comments like <!-- prettier-ignore --> are pretty common and having to remove them might cause issues down the line.

@r4pt0s Cant thank you enough for this.

I tried what @dacxjo mentioned in a standard project created with @vue/cli 4.5.13 which works with webpack.

Somehow, the issue only exists while developing with the webpack dev server.

Once the project is build with yarn run build

the finished build is working properly.

The issue only exists by using

mode="out-in"

on the transition component

To me it looks like a webpack config issue but I could be wrong