Vue-next: Transition not working with custom dynamic components

Created on 1 Apr 2020  路  7Comments  路  Source: vuejs/vue-next

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/

bug transition

Most helpful comment

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/

All 7 comments

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

Great! I don't know if that's the right fix but it would be nice if it had some unit tests

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/

Hi! How I can add a transition tag in the render function? It works fine in the Vue-2. But it doesn't work in Vue-3
setup() {
...
...
return () => h('transition', {name: 'fade'}, [VNode])
}
It creates the transition tag in DOM

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;
}

@gregg-cbs you were maybe on the old docs. The ones for vue-router for Vue 3 are at https://next.router.vuejs.org/guide/advanced/transitions.html#transitions

Thanks @posva !

This docs thing is a tough one isnt it. I'm finding it very difficult to find information.
Google doesn't show any pages from https://next.router.vuejs.org/ when you google Vue 3 Router questions.

Even v3.vuejs.org is not coming up in any of my searches.

But Vue 3 youtube videos and blog articles and stackoverflow things are popping up because the have the term Vue 3 in their headings.

Making it tough to transition from 2 to 3 at times because the docs is what we should be finding.
Perhaps in future releases we should make it more explicit:
vuejs.org/vue-2
vuejs.org/vue-3

But Vue is free and I am not complaining, I am mentioning this pitfall for future consideration.

Was this page helpful?
0 / 5 - 0 ratings