You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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>

问题原因

  1. Transition无法感知组件变化:路由切换时,Component引用本身未改变(仅组件内部路由参数变化),Transition认为无需触发过渡。
  2. 目标组件代码存在语法错误:watchEffect内的异步代码缺少闭合括号,可能导致逻辑异常。
  3. 组件实例复用: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 14:20:37