情景
整个情形非常简单
<template>
<router-view v-slot="{ component }">
<transition>
<component :is="component" />
</transition>
</router-view>
</template>
一个非常简单的自定义路由,我现在要给他加一个动画。
这里我采用的是 @leave 这个事件,绑定到 transition 上,并且我把 transition 封装成了一个自定义组件
<Transition
@before-enter="onBeforeEnter"
@enter="onEnter"
@after-enter="onAfterEnter"
@enter-cancelled="onEnterCancelled"
@before-leave="onBeforeLeave"
@leave="onLeave"
@after-leave="onAfterLeave"
@leave-cancelled="onLeaveCancelled"
>
<!-- ... -->
</Transition>
问题
现在,问题就来了。
- 当我切换页面的时候,发现和 leave 相关的都没用,都没有触发。
排查
首先,我考虑的是 缓存,是不是因为 component 重复,导致没有触发更改。(参考 v-for key,还有 scroll 的 key)
所以,我给 component 加了一个 key
类似
<script>
export default {
data() {
return {
count: 1,
interval: null
}
},
mounted() {
this.interval = setInterval(() => {
this.count++;
}, 1000)
},
beforeDestroy() {
clearInterval(this.interval)
}
}
</script>
<template>
<Transition>
<span :key="count">{{ count }}</span>
</Transition>
</template>
然后发现并不是这个问题。
几经周折之下,我就去看了一下 vue transition 的代码
这个 transition 其实是基于 BaseTransition 的
我放到这里
我们可以发现,是因为触发了 onBeforeUnmount 这个生命周期,导致后续的事件触发走了优化,不必再执行繁琐的动画。但是对于我来说,我需要这个动画。
其实走到这里,解决方案就一目了然了。就是组件被销毁了,就不用再执行复杂的动画了。
解决方案
- keep-alive 这是最简单的解决方案。
<keep-alive>
<transition>
<component />
</transition>
</keep-alive>
- 组件播放动画 我们可以在组件退出之前,手动给组件播放动画,然后再调用真正的退出。
// 示例
async function exitWithAnimation() {
await playLeaveAnimation(); // 播放动画
router.back(); // 再进行返回
}
即,假设我们采用 router.back() 做退出处理。
我们可以先 播放动画 再 执行这个 back
希望 vue 官方能增加一个属性关闭这个优化。