效果演示

使用的API和类库
Transition、router-view
可选:keep-alive、pinia等
关键代码
<template>
<router-view v-slot="{ Component }">
<transition :name="transitionName">
<component :is="Component" />
</transition>
</router-view>
</template>
<script setup>
import { ref } from 'vue'
const transitionName=ref('')
window.will_push = () => {
transitionName.value='forward'
}
window.will_back = () => {
transitionName.value='back'
}
</script>
<style>
/* ================= 前进动画 ================= */
/* 进入的元素动画执行时样式 */
.forward-enter-active {
}
/* 进入的元素动画初始样式 */
.forward-enter-from {
}
/* 进入的元素动画结束时样式 */
.forward-enter-to {
}
/* 离开的元素动画执行时样式 */
.forward-leave-active {
}
/* 离开的元素动画初始样式 */
.forward-leave-from {
}
/* 离开的元素动画结束时样式 */
.forward-leave-to {
}
/* ================= 后退动画 ================= */
/* 进入的元素动画执行时样式 */
.back-enter-active {
}
/* 进入的元素动画初始样式 */
.back-enter-from {
}
/* 进入的元素动画结束时样式 */
.back-enter-to {
}
/* 离开的元素动画执行时样式 */
.back-leave-active {
}
/* 离开的元素动画初始样式 */
.back-leave-from {
}
/* 离开的元素动画结束时样式 */
.back-leave-to {
}
</style>
设置、触发动画的方法
在需要添加动画的路由切换时,先调用window.will_push()或window.will_back(),再调用router.push()或router.back(),即可稳定、灵活地触发切换动画。
不在router.beforeEach中改变transitionName值的原因
触发路由跳转时的执行流程如下:
同步任务 (Tick 1):
↓ 用户点击路由链接 / router.push()
↓ beforeEach 守卫执行 ← 修改 ref(值变了,但 DOM 未更新)
↓ beforeRouteLeave 守卫
↓ beforeRouteEnter 守卫
↓ 路由确认完成
微任务队列 (Tick 1 结束后):
↓ transitionName 的响应式更新触发
↓ 计算属性重新计算
↓ 准备组件更新
下一个 Tick (Tick 2):
↓ <router-view> 开始卸载旧组件
↓ <Transition> 组件初始化,读取 props(包括 name)
↓ 此时才用到 transitionName 的"新值"
问题在于:Transition 可能在前一个 tick 就已经读取了旧值,或者更新时机与组件卸载/挂载不匹配。尤其是旧组件的退出动画,很可能不会正确执行。
贴别是配合使用keep-alive的情况
<router-view v-slot="{ Component }">
<transition :name="transitionName">
<keep-alive :include="pageKeepAlive.names">
<component :is="Component" />
</keep-alive>
</transition>
</router-view>
实际使用中,很可能还要和keep-alive一并使用,并且keep-alive的include也需要动态绑定,这种情况更复杂。所以,完全手动控制transition的name,并在路由跳转开始前就修改好transitionName的值是一种比较稳妥的办法。为减少代码冗余,可以自行封装router.push等方法,预置transitionName即可。
其他需要注意的事项
-
window.will_push等挂载到全局上的方法,也可以使用pinia代替,transitionName使用计算属性即可。 -
在动画样式中,如需要实现页面整体平移等transform,需要在
v-enter-active、v-leave-active中添加position: absolute !important,通过绝对定位控制位移动画。 -
关于
Transition的mode,官方文档中有说明,需要两个页面同时存在(进入和退出动画同时执行)的情况,mode无需设置保持默认即可,另外的情况按需修改。 -
useRoute()中meta等响应式数据,会在路由切换最开始的那一刻就立即切换为新页面(to)的内容。如果你的项目中旧页面(from)中的某些元素或属性依赖了useRoute()中的信息,在路由切换开始时,可能会使旧页面的某些元素发生切换或闪烁,或触发预料外的逻辑,最好的办法是使用额外的变量储存初始化时的useRoute()中的信息,不要直接依赖useRoute()。

