Vue3路由跳转后onMounted获取到旧路由的问题求助
问题:Vue动态导航页面onMounted获取到旧路由路径
在Vue项目中采用后端驱动的动态导航方案,通过接口根据当前路由决定渲染的页面。但在使用<router-link>或router.push跳转时,目标页面的onMounted回调中获取到的是上一个路由的路径(比如跳转到/about时,AboutView里打印的是/而非/about)。尝试过await router.isReady(),但该方法仅在初始页面加载时有效,跳转场景下不起作用。
相关代码
App.vue
<template> <router-view/> </template>
router.ts
export const currentPage = ref('HomeView') const router = createRouter({ history: createWebHistory(import.meta.env.BASE_URL), routes: [ { name: "PageSelector", path: "/:catchAll(.*)", component: PageSelector } ] }); router.beforeEach(async (to, _, next) => { currentPage.value = await getComponentByPath(to.path) next() }) async function getComponentByPath(path: string) { return path === '/' ? 'HomeView' : 'AboutView' }
PageSelector.vue
<template> <component :is="currentPage" /> </template> <script lang="ts"> import { defineComponent } from "vue" import AboutView from "@/views/AboutView.vue" import HomeView from "@/views/HomeView.vue" import { currentPage } from "@/router" export default defineComponent({ // eslint-disable-next-line vue/no-unused-components components: { HomeView, AboutView }, setup() { return { currentPage } } }) </script>
AboutView.vue
<template> about </template> <script lang="ts"> import router from "@/router"; import { defineComponent, onMounted } from "vue"; export default defineComponent({ setup() { onMounted(() => { console.log(router.currentRoute.value?.fullPath) // prints / instead of /about // when clicking a router link or router push to this page }) } }) </script>
问题原因
核心是路由跳转时机与组件生命周期的执行顺序冲突:
router.beforeEach中更新currentPage后调用next(),此时路由实例的currentRoute还未完成更新next()执行后,PageSelector立刻开始渲染目标组件(如AboutView),而组件的onMounted在路由状态完全更新前就触发了,所以拿到的是旧路由信息router.isReady()仅负责等待应用初始化时的路由就绪,对后续的路由跳转无监听等待效果
解决方案
方案1:组件内监听路由变化,确保获取最新路径
修改AboutView.vue,通过watch监听路由的fullPath变化,既能捕获跳转后的最新路径,初始化时也能执行:
<template> about </template> <script lang="ts"> import router from "@/router"; import { defineComponent, watch } from "vue"; export default defineComponent({ setup() { watch( () => router.currentRoute.value.fullPath, (newPath) => { console.log(newPath) // 打印正确的目标路径 }, { immediate: true } // 初始化时立即执行一次 ) } }) </script>
如果一定要在onMounted中处理,可结合路由的afterEach等待路由跳转完成:
<script lang="ts"> import router from "@/router"; import { defineComponent, onMounted } from "vue"; export default defineComponent({ setup() { onMounted(async () => { await router.afterEach(() => {}).finished console.log(router.currentRoute.value.fullPath) }) } }) </script>
方案2:路由完成更新后再切换组件
修改router.ts,将更新currentPage的逻辑移到afterEach中,确保路由状态完全更新后再渲染目标组件:
export const currentPage = ref('HomeView') const router = createRouter({ history: createWebHistory(import.meta.env.BASE_URL), routes: [ { name: "PageSelector", path: "/:catchAll(.*)", component: PageSelector } ] }); // 路由跳转完成后再切换页面组件 router.afterEach(async (to) => { currentPage.value = await getComponentByPath(to.path) }) // 初始化时先设置默认页面 router.isReady().then(() => { currentPage.value = getComponentByPath(router.currentRoute.value.path) }) async function getComponentByPath(path: string) { return path === '/' ? 'HomeView' : 'AboutView' }
同时给PageSelector.vue加个加载状态,避免初始渲染空白:
<template> <component v-if="currentPage" :is="currentPage" /> <div v-else>加载中...</div> </template>
方案3:通过路由元信息传递目标路径
在beforeEach中把目标路由路径存入元信息,组件直接读取即可:
router.ts修改:
router.beforeEach(async (to, _, next) => { currentPage.value = await getComponentByPath(to.path) to.meta.targetPath = to.path next() })
AboutView.vue中读取:
<script lang="ts"> import router from "@/router"; import { defineComponent, onMounted } from "vue"; export default defineComponent({ setup() { onMounted(() => { console.log(router.currentRoute.value.meta.targetPath) // 直接获取正确路径 }) } }) </script>
内容的提问来源于stack exchange,提问作者Johan
相关产品推荐
相关产品推荐

