I'm seeing some behavior with Vue transitions that I don't understand. It'll be easier to explain by showing an example:
Given the following component:
<transition :name="transitionName">
<div v-if="showMe">Hey</div>
</transition>
<button @click="transitionName='slide-left'; showMe = false">Left</button>
And that the following is true:
- There are css classes in place for the transition names
.slide-leftand.slide-rightwhich does what their names imply. - The initial state of
transitionNameisslide-right - The initial state of
showMeis true
I would expect the div to slide left when the button is clicked. However, it slides right.
A full reproducible is available here:
var demo = new Vue({
el: '#demo',
data: {
showMe: true,
transitionName: 'slide-right'
}
});
#demo {
display: flex;
justify-content: center;
flex-direction: column;
width: 20%;
margin: 100px auto;
text-align: center;
}
.buttons {
display: flex;
justify-content: space-between;
}
.slide-right-enter-active,
.slide-left-enter-active,
.slide-right-leave-active,
.slide-left-leave-active {
transition: all 1s ease-in-out;
}
.slide-left-enter,
.slide-left-leave-to {
transform: translateX(-100%);
}
.slide-right-enter,
.slide-right-leave-to {
transform: translateX(100%);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="demo">
<transition :name="transitionName">
<div v-if="showMe">Hey</div>
</transition>
<div class="buttons">
<button @click="transitionName='slide-left'; showMe = false">left</button>
<button @click="transitionName='slide-right'; showMe = !showMe">right</button>
</div>
</div>
It contains both a "left" and "right" button, but the component will slide "right" regardless of which one you click.
I can fix it by changing the button's callback to this:
this.transitionName = 'slide-left';
Vue.nextTick(() => {
this.showMe = false;
});
But I'm wondering why nextTick is necessary here, and whether there is a better way to solve the problem in general.
It's because of Vue's update cycle. It queues all the updates and 'flushes' them at the same time so it can evaluate which sections of the page need to be changed and only patch those changes. When you use
Vue.nextTickyou're essentially telling Vue to wait until the next update cycle before settingthis.showMe = false.The docs explain it better than I do: