Vue路由动态导入引发默认布局短暂闪烁的解决方案咨询
我使用Vue 3结合vue-router与vite-plugin-pages开发项目,该插件默认异步加载所有路由组件。我制作了一个布局组件,可根据路由meta在LayoutDefault和LayoutBlank间切换——Blank布局仅用于登录页这类公共页面,以此减少布局配置量。但因动态导入机制,进入站点经beforeEach守卫重定向到登录页时,会短暂显示默认布局再切换为Blank布局,原因是初始时监听器中的route.meta.layout为undefined。我不想将/login设为同步加载,后续可能添加更多需Blank布局的公共路由,直接访问这类路由也会出现闪烁问题。除了将Blank设为默认布局或全同步加载外,还有其他解决方法吗?我曾尝试用另一个beforeEach钩子替代监听route.meta.layout,但仅将闪烁转移到路由离开时。
1. 初始化时解析最终路由的meta信息
利用vue-router的router.resolve方法,在布局组件初始化时直接获取最终要跳转的路由(包括重定向后的路由)的meta信息,避免初始状态下meta为undefined导致的默认布局闪烁。
修改后的AppLayout.vue代码:
<script setup lang="ts"> import AppLayoutDefault from './AppLayoutDefault.vue' import AppLayoutBlank from './AppLayoutBlank.vue' import { markRaw, ref, onMounted, watch } from 'vue' import { useRoute, useRouter } from 'vue-router' const layout = ref() const route = useRoute() const router = useRouter() const setLayout = (targetRoute) => { const metaLayout = targetRoute.meta?.layout as string | undefined layout.value = markRaw(metaLayout === 'blank' ? AppLayoutBlank : AppLayoutDefault) } // 组件挂载时先解析最终目标路由 onMounted(() => { const resolvedRoute = router.resolve(route.fullPath) setLayout(resolvedRoute) }) // 监听路由切换时的meta变化 watch( () => route.meta?.layout as string | undefined, (metaLayout) => { layout.value = markRaw(metaLayout === 'blank' ? AppLayoutBlank : AppLayoutDefault) } ) </script> <template> <component :is="layout"> <router-view /> </component> </template>
2. 路由守卫中提前设置布局状态
通过全局状态提前在路由守卫中确定目标路由的布局类型,布局组件直接读取该状态,无需等待路由meta的异步更新。
步骤1:创建全局布局状态
新建src/composables/useLayoutState.ts:
import { ref } from 'vue' export const useLayoutState = () => { const currentLayout = ref<string | undefined>() return { currentLayout } }
步骤2:修改路由守卫
更新src/router.ts:
import { createRouter, createWebHistory } from 'vue-router' import Home from '@/views/Home.vue' import { useLayoutState } from '@/composables/useLayoutState' const router = createRouter({ history: createWebHistory(), routes: [ { path: '/', component: Home, }, { path: '/login', component: () => import('@/views/Login.vue'), meta: { layout: 'blank', public: true }, }, { path: '/admin', component: () => import('@/views/Admin.vue'), meta: { layout: 'AppLayoutAdmin' }, }, ], }) let isLoggedIn = false const { currentLayout } = useLayoutState() router.beforeEach((to) => { // 提前设置目标路由的布局类型 currentLayout.value = to.meta?.layout as string | undefined if (!to.meta.public && !isLoggedIn) { isLoggedIn = true return 'login' } }) export default router
步骤3:修改布局组件
更新AppLayout.vue:
<script setup lang="ts"> import AppLayoutDefault from './AppLayoutDefault.vue' import AppLayoutBlank from './AppLayoutBlank.vue' import { markRaw, ref, watch } from 'vue' import { useLayoutState } from '@/composables/useLayoutState' const layout = ref() const { currentLayout } = useLayoutState() watch( currentLayout, (metaLayout) => { layout.value = markRaw(metaLayout === 'blank' ? AppLayoutBlank : AppLayoutDefault) }, { immediate: true } ) </script> <template> <component :is="layout"> <router-view /> </component> </template>
3. 添加初始加载兜底状态
如果上述方法仍有闪烁问题,可以给布局组件添加初始空白状态,和Blank布局背景保持一致,直到布局确定后再显示内容:
<template> <!-- 初始空白兜底,避免闪烁 --> <div v-if="!layout" class="layout-placeholder"></div> <component v-else :is="layout"> <router-view /> </component> </template> <style scoped> .layout-placeholder { width: 100vw; height: 100vh; background-color: #ffffff; /* 和Blank布局背景匹配 */ } </style>
内容的提问来源于stack exchange,提问作者hazelnutek

