diff --git a/examples/blog/docs/notes/前端/vue-router-mount-timing.md b/examples/blog/docs/notes/前端/vue-router-mount-timing.md new file mode 100644 index 00000000..11b4d499 --- /dev/null +++ b/examples/blog/docs/notes/前端/vue-router-mount-timing.md @@ -0,0 +1,162 @@ +--- +title: Vue App 挂载与 Router beforeEach 时序关系 +createTime: 2026/04/12 12:05:23 +tags: + - Vue + - Vue Router +--- + +## 问题背景 + +在使用 Vue 3 + Vue Router 开发应用时,遇到了一个**时序问题**: + +::: warning 现象 +`App.vue` 中的 `onMounted` 钩子**先于** `router.beforeEach` 路由守卫执行,导致在路由守卫中进行的异步权限校验还未完成时,页面逻辑已经开始执行。 +::: + +## 问题复现 + +```ts title="main.ts" +import { createApp } from "vue"; +import App from "./App.vue"; +import router from "./router"; + +const app = createApp(App); + +app.use(router); // 安装路由 +app.mount("#app"); // 挂载应用 +``` + +```ts title="router/index.ts" +router.beforeEach(async (to, from) => { + // 异步请求:获取用户权限 + await fetchUserPermission(); // 假设耗时 500ms + // ... ... +}); +``` + +```vue title="App.vue" + +``` + +## 问题分析 + +按照 Vue Router 文档,标准执行顺序应该是: + +```text +app.use(router) → app.mount() → beforeEach → 组件挂载 +``` + +但在 `beforeEach` 中加入异步请求后,执行顺序变成了: + +```text +app.use(router) → app.mount() → App.vue mounted → beforeEach 完成 +``` + +`beforeEach` 虽然是异步解析,但它**不会阻塞**根组件 `App.vue` 的同步挂载流程。 +`beforeEach` 守卫**只阻塞路由匹配的组件创建**,如果路由组件直接配置为 `App.vue` ,`
`的挂载并不会被阻塞。根本原因在于根组件 `App.vue` 在 `app.mount()` 时已经**同步创建和挂载** + +## 解决方案 + +把业务逻辑拆分,`App.vue`只作为静态挂载点存在,实际业务逻辑用`` 控制,`` 的创建和渲染可以被正常阻塞,`App.vue` 自身的生命周期不受影响。 + +```vue title="App.vue(现在只是空壳)" + + + +``` + +```json title="router/index.ts" +{ + path: '/', + component: () => import('./views/Layout/MainLayout.vue'), + children: [ + { path: '', component: Home }, + { path: 'about', component: About } + ] +} +``` + +```vue title="views/Layout/MainLayout.vue(业务逻辑在这里)" + + + +``` + +## 其它方案 + +### 挂载前完成异步初始化 + +将关键异步逻辑提前到挂载之前: + +```ts title="main.ts" +async function init() { + // 1. 先完成异步初始化 + await fetchUserPermission(); + + // 2. 再安装路由 + app.use(router); + + // 3. 最后挂载 + app.mount("#app"); +} + +init(); +``` + +### 使用 `router.isReady()` + +在 `App.vue` 中等待路由就绪: + +```vue title="App.vue" + + + +``` + +### 使用 `router.beforeResolve` + +`router.beforeResolve` 和 `router.beforeEach` 类似,因为它在每次导航时都会触发,不同的是,解析守卫刚好会在导航被确认之前、所有组件内守卫和异步路由组件被解析之后调用。 + +## 相关链接 + +- [Vue Router 导航守卫官方文档](https://router.vuejs.org/zh/guide/advanced/navigation-guards.html) +- [Vue 3 生命周期钩子](https://cn.vuejs.org/api/composition-api-lifecycle.html)