Vue Suspense仅直接访问异步组件时生效,路由跳转无加载提示
问题描述
项目包含Home和Tools两个路由:直接访问Tools路由时,Suspense的“Loading...”提示正常显示;但从Home路由跳转至Tools路由时,尽管异步组件需要加载2秒,加载提示却不显示。期望无论从哪个路由进入,加载异步组件时都能显示加载提示。
相关代码
App.vue
<script setup> import Connexion from '@/components/Connexion.vue' import { ref } from 'vue' const drawer = ref(null); </script> <template> <v-app> <v-navigation-drawer expand-on-hover rail> <Suspense> <Connexion /> <template #fallback> Chargement... </template> </Suspense> <v-divider></v-divider> <v-list density="compact" nav> <v-list-item prepend-icon="mdi-home" title="Home" value="home" @click="$router.push('/')"></v-list-item> <v-list-item prepend-icon="mdi-account-multiple" title="Tools" value="tools" @click="$router.push('/tools')"></v-list-item> </v-list> </v-navigation-drawer> <v-app-bar> <v-toolbar-title>My app</v-toolbar-title> </v-app-bar> <v-main fluid fill-width> XXX <v-container fluid > <router-view v-slot="{ Component }"> <template v-if="Component"> <Transition mode="out-in"> <keep-alive> <Suspense> <component :is="Component" /> <template #fallback> Loading... </template> </Suspense> </keep-alive> </Transition> </template> </router-view> </v-container> </v-main> </v-app> </template> <style> .v-enter-active, .v-leave-active { transition: opacity 0.2s ease; } .v-enter-from, .v-leave-to { opacity: 0; } </style>
Tools.vue
<script> import { defineComponent } from "vue"; export default defineComponent({ async setup() { const apiUrl = import.meta.env.VITE_APP_API_URL; console.log("loading...") const demandes = await fetch(`${apiUrl}/tools`) .then((r) => r.json()) .catch((e) => { console.log(e); return []; }); await new Promise(r => setTimeout(r, 2000)); console.log("LOADED!") return { demandes, } } }) </script> <template> <div> <h1>Tools</h1> <ul> <li v-for="tool in tools" :key="`tool-${tool.id}`" > {{ tool }} </li> </ul> </div> </template>
路由配置
import { createRouter, createWebHistory } from 'vue-router'; import { nextTick } from 'vue'; const routes = [ { path: '/', name: 'Home', component: () => import('@/views/Home.vue'), meta: { title: 'Home', }, }, { path: '/tools', name: 'Tools', component: () => import('@/views/Tools.vue'), meta: { title: 'Tools', }, }, ]; const router = createRouter({ base: import.meta.env.VITE_APP_PUBLICPATH, history: createWebHistory(import.meta.env.VITE_APP_BASE_URL), routes, }); const DEFAULT_TITLE = import.meta.env.VITE_APP_TITLE; router.afterEach((to) => { nextTick(() => { document.title = to.meta.title ? `${DEFAULT_TITLE} - ${to.meta.title}` : DEFAULT_TITLE; }); }); export default router;
Home.vue
<script setup> </script> <template> <h1>Home</h1> </template> <style scoped> </style>
解决方案
问题根源是<keep-alive>缓存了Tools组件,首次加载后,从Home跳转回Tools时,组件不会重新执行setup里的异步逻辑,导致Suspense无法触发fallback状态。以下是几种解决方式:
方法1:移除不必要的<keep-alive>
如果不需要缓存Tools组件,直接删除<keep-alive>标签:
<Transition mode="out-in"> <Suspense> <component :is="Component" /> <template #fallback> Loading... </template> </Suspense> </Transition>
方法2:控制<keep-alive>的缓存范围
给<keep-alive>添加include属性,只缓存Home组件(需给Home组件定义name属性):
<keep-alive include="Home"> <Suspense> <component :is="Component" /> <template #fallback> Loading... </template> </Suspense> </keep-alive>
方法3:组件激活时重新触发异步逻辑
保留<keep-alive>,修改Tools.vue,将异步逻辑封装成函数,在activated钩子中调用,并手动管理加载状态:
<script> import { defineComponent, ref } from "vue"; export default defineComponent({ name: 'Tools', setup() { const demandes = ref([]); const isLoading = ref(false); const loadData = async () => { isLoading.value = true; const apiUrl = import.meta.env.VITE_APP_API_URL; console.log("loading...") demandes.value = await fetch(`${apiUrl}/tools`) .then((r) => r.json()) .catch((e) => { console.log(e); return []; }); await new Promise(r => setTimeout(r, 2000)); console.log("LOADED!") isLoading.value = false; } return { demandes, isLoading, loadData } }, async activated() { await this.loadData(); }, async mounted() { await this.loadData(); } }) </script> <template> <div v-if="isLoading">Loading...</div> <div v-else> <h1>Tools</h1> <ul> <li v-for="tool in demandes" :key="`tool-${tool.id}`" > {{ tool }} </li> </ul> </div> </template>
方法4:路由元信息控制缓存
在路由配置中给Tools添加meta: { keepAlive: false },然后在App.vue中动态判断是否缓存:
<Transition mode="out-in"> <keep-alive v-if="$route.meta.keepAlive"> <Suspense> <component :is="Component" /> <template #fallback> Loading... </template> </Suspense> </keep-alive> <Suspense v-else> <component :is="Component" /> <template #fallback> Loading... </template> </Suspense> </Transition>
路由配置修改:
{ path: '/tools', name: 'Tools', component: () => import('@/views/Tools.vue'), meta: { title: 'Tools', keepAlive: false }, },
内容的提问来源于stack exchange,提问作者djcaesar9114
相关产品推荐
相关产品推荐

