场景
在使用Vue提供的transition组件来实现页面切换时的过渡效果时,直接使用了transition来包裹路由,结果发现了一个问题,新页面进入时的动画效果成功实现了,而旧页面离开的动画却失效了。
子页面1:
Page1.Vue
<template>
<div class="page1">
page1
</div>
</template>
<style scoped>
.page1 {
width: 100%;
height: 100%;
border: 1px solid #000;
background-color: pink;
text-align: center;
font-size: 50px;
}
</style>
子页面2:
Page2.vue
<template>
<div class="page2">
page2
</div>
</template>
<style scoped>
.page2 {
width: 100%;
height: 100%;
background-color: blue;
text-align: center;
font-size: 50px;
}
</style>
主页面:
App.vue
<template>
<div class="container">
<div class="tabs">
<router-link to="/page1">page1</router-link>
<router-link to="/page2">page2</router-link>
</div>
<div class="page">
<transition name="fade" mode="out-in">
</router-view>
</transition>
</div>
</div>
</template>
<style>
.container {
margin: 0 auto;
width: 800px;
height: 600px;
border: 1px solid #000;
}
.page {
width: 100%;
height: calc(100% - 60px);
}
.tabs {
height: 40px;
width: 200px;
margin: 0 auto;
background: green;
display: flex;
justify-content: center;
align-items: center;
gap: 20px;
margin-bottom: 20px;
}
a {
color: #fff;
font-size: 20px;
}
.fade-enter-active {
transition: all 0.5s ease-in-out;
}
.fade-enter-from {
opacity: 0;
transform: translateX(100%);
}
.fade-enter-to {
opacity: 1;
transform: translateX(0);
}
.fade-leave-active {
transition: all 0.5s ease-in-out;
}
.fade-leave-from {
opacity: 1;
transform: translateX(0);
}
.fade-leave-to {
opacity: 0;
transform: translateX(-100%);
}
</style>
页面效果如下:
切换页面 .fade-enter相关CSS成功执行,.fade-leave相关CSS执行失败.(如图) 
解决方法
后来我从别人那获取到了解决方法:用 RouterView 包裹 Transition 配合 Component 实现过渡效果。
vue
<template>
<router-view v-slot="{ Component }">
<transition>
<component :is="Component" />
</transition>
</router-view>
<template/>
改为这样子后,无论是进入还是里离开都能正确执行了。 
原因
Vue3 官方文档 Transition一节中,给出了transition组件的触发条件(满足其一):
- 由
v-if所触发的切换 - 由
v-show所触发的切换 - 由特殊元素
<component>切换的动态组件 - 改变特殊的
key属性
意思就是说<transition> 组件要正常工作,包裹的内容必须是 "可复用" 的元素或组件
问题根源:router-view 是一个 "动态渲染出口"
其关键在于router-view本身不可复用,它的核心行为如下:
- 当路由切换时,销毁旧组件实例 ,创建新组件实例;
router-view本身不会 "更新",而是直接替换内部的组件内容。
当直接写 <transition><router-view /></transition> 时,路由更新,transition还没有来得及给旧组件添加离开的效果时,旧的组件实例已经销毁了,这时新的组件实例创建,transition能够正常捕获到该组件实例。而解决方法是通过作用域插槽 "v-slot={Component}" ,把当前路由对应的组件实例暴露出来, 然后使用 component来动态渲染,由于 <component> 本身是一个 "可复用" 的容器,它不会被销毁,只是改变内部渲染的组件。可以让 <transition> 正常监听组件的 "离开" 和 "进入",从而执行完整的过渡动画。
总结
总之,路由切换过渡动画的核心是让 <transition> 能正常捕获组件的 "离开" 与 "进入" 状态(满足官方给出的触发条件)。避开直接包裹 <router-view> 的误区,同时呢也需要注意样式隔离、动画执行顺序等细节,这样就能实现完整的路由过渡效果。