Vue路由过渡仅在加载/刷新时生效,路由切换无动画问题
Vue路由切换时Suspense+Transition动画不生效的解决方案
问题场景
按照Vue官方文档方式,在Suspense组件外层添加Transition实现路由跳转过渡效果,但动画仅在页面加载或单独刷新页面时生效,点击RouterLink切换路由时无动画,控制台无报错。
相关代码
主App.vue
<template> <NavigationBar /> <RouterView v-slot="{ Component }"> <template v-if="Component"> <Transition name="scale" mode="out-in"> <suspense> <template #default> <div class="wl"> <component :is="Component"></component> </div> </template> <template #fallback>Loading..</template> </suspense> </Transition> </template> </RouterView> </template> <script setup> import NavigationBar from './components/NavigationBar.vue'; </script> <style lang="scss"> // 其他样式 // 过渡样式 .scale-enter-active, .scale-leave-active { transition: all 0.5s ease; } .scale-enter-from, .scale-leave-to { opacity: 0; transform: scale(0.8); } </style>
首页路由组件(简化版)
<template> <router-link :to="countryCode"> <div class="card" :key="$route.params.code"> <h2>{{ countryName }}</h2> </div> </router-link> </template> <script setup> import { ref } from 'vue'; const props = defineProps({ countryData: { type: Object, required: true, }, }); const countryName = ref(props.countryData.name.common); </script> <style scoped lang="scss"> // 组件样式 </style>
目标视图组件
<template> <RouterLink to="/" class="backLink"> <img src="@/assets/arrow-back-outline.svg" alt="返回按钮" /> <button>返回</button> </RouterLink> <div class="country" :key="$route.params.code"> {{ name }} </div> </template> <script setup> import { ref, watchEffect } from 'vue'; import { useRoute } from 'vue-router'; import CountriesApi from '@/services/CountriesApi'; const route = useRoute(); const countryData = ref( await CountriesApi.getCountryByCode(route.params.code).catch(() => null) ); const name = ref(''); name.value = countryData.value[0].name.common; watchEffect(async () => { countryData.value = await CountriesApi.getCountryByCode(route.params.code) name.value = countryData.value[0].name.common; }); </script>
问题原因
- Transition无法感知组件变化:路由切换时,
Component引用本身未改变(仅组件内部路由参数变化),Transition认为无需触发过渡。 - 目标组件代码存在语法错误:
watchEffect内的异步代码缺少闭合括号,可能导致逻辑异常。 - 组件实例复用:Vue Router默认复用同一路径下的组件实例,进一步阻止Transition触发。
解决方案
1. 给Transition内的根元素绑定路由唯一标识
在App.vue中,给Suspense默认插槽的根div添加:key="$route.fullPath",强制Vue重新渲染组件,触发Transition:
<template> <NavigationBar /> <RouterView v-slot="{ Component }"> <template v-if="Component"> <Transition name="scale" mode="out-in"> <suspense> <template #default> <!-- 绑定路由完整路径作为唯一key --> <div class="wl" :key="$route.fullPath"> <component :is="Component"></component> </div> </template> <template #fallback>Loading..</template> </suspense> </Transition> </template> </RouterView> </template>
2. 修复目标组件的语法错误并优化数据获取
将数据获取逻辑封装为独立函数,避免顶层await的潜在问题,同时修复语法错误:
<script setup> import { ref, watchEffect } from 'vue'; import { useRoute } from 'vue-router'; import CountriesApi from '@/services/CountriesApi'; const route = useRoute(); const countryData = ref(null); const name = ref(''); // 封装数据获取函数 async function fetchCountry() { try { const res = await CountriesApi.getCountryByCode(route.params.code); countryData.value = res; name.value = res[0].name.common; } catch (err) { countryData.value = null; name.value = ''; } } // 初始化数据 await fetchCountry(); // 监听路由参数变化重新获取数据 watchEffect(async () => { await fetchCountry(); }); </script>
3. 可选:禁用组件复用(如果需要)
如果希望路由切换时完全销毁重建组件,可以在路由配置中添加props: true或绑定唯一key:
// router/index.js const routes = [ { path: '/country/:code', component: () => import('../views/CountryView.vue'), props: true } ];
效果说明
绑定$route.fullPath作为key后,任何路由变化(包括参数、查询参数变更)都会触发Transition的过渡动画,同时修复后的异步逻辑确保Suspense能正确处理加载状态。
内容的提问来源于stack exchange,提问作者anas ali
相关产品推荐
相关产品推荐

