前言
进阶不等于复杂,而是更加精细的控制。掌握守卫的完整流程、动态路由、数据获取策略以及几个实用的配置项(滚动、过渡、元信息),你就能把 Vue Router 用得游刃有余。遇到疑难,退回本源思考"何时、何地、如何"触发导航,一切都会清晰。
1. 导航守卫全流程解析
整个路由跳转的守卫调用顺序,可以用一句话概括:从全局到路由,再到组件;从"离开"到"进入"。
- 导航触发
- 在失活的组件里调用
beforeRouteLeave - 调用全局
beforeEach - 在可重用的组件里调用
beforeRouteUpdate(如/user/1→/user/2) - 在路由配置里调用
beforeEnter(路由独享守卫) - 解析异步路由组件
- 在被激活的组件里调用
beforeRouteEnter - 调用全局
beforeResolve - 导航被确认
- 调用全局
afterEach(没有next,不能阻止) - 触发 DOM 更新
- 执行
beforeRouteEnter传给next的回调(此时组件实例已创建)
关键点 :
beforeResolve是在所有组件内守卫和异步组件解析后,导航确认前调用的,适合做最终权限验证。
2. 组合式 API 中的守卫
在 setup 里使用组合式 API,不再有选项式钩子,改用导入的函数:
js
import { onBeforeRouteLeave, onBeforeRouteUpdate } from 'vue-router'
export default {
setup() {
onBeforeRouteLeave((to, from) => {
// 离开时提示保存
const answer = window.confirm('有未保存的更改,确定离开吗?')
if (!answer) return false
})
}
}
beforeRouteEnter在组合式 API 里没有直接等价函数(因为setup执行时组件还未创建),但可以通过在全局守卫或路由独享守卫中处理,或沿用选项式。
3. 路由组件复用与 key
当同一个组件被复用时(如 /user/123 到 /user/456),默认不会重新创建,只是更新 $route。如果想强制重建,可以给 <router-view> 绑定 key:
html
<router-view :key="$route.fullPath" />
这会让每个路径都对应一个全新实例,适用于带生命周期的复杂组件。
4. 数据获取策略
两种主要模式,根据 UX 选择:
-
导航后获取 :先跳转,组件显示 loading 状态再请求数据。
使用
watch监听$route变化,或利用beforeRouteUpdate。体验更顺滑。jswatch(() => route.params.id, async (newId) => { data.value = await fetchUser(newId) }) -
导航前获取 :在
beforeRouteEnter或路由守卫中获取,数据拿到后再放行。可以避免看到空白页。jsbeforeRouteEnter(to, from, next) { fetchUser(to.params.id).then(user => { next(vm => vm.user = user) }) }
5. 动态路由:按需注册
后台管理系统的权限路由通常需要异步添加。使用 router.addRoute() 可以动态注入路由,甚至嵌套子路由。
js
// 添加一条顶层路由
router.addRoute({ path: '/admin', component: Admin })
// 为已命名的路由添加子路由
router.addRoute('admin', { path: 'users', component: AdminUsers })
注意:新增路由后需手动导航到目标页(如 router.push 刷新匹配),且防止重复添加可以先用 router.hasRoute() 判断。
6. 过渡动效与滚动行为
过渡动效 :给 router-view 套上 Vue 自带的 <Transition>。
html
<router-view v-slot="{ Component, route }">
<transition :name="route.meta.transition || 'fade'" mode="out-in">
<component :is="Component" />
</transition>
</router-view>
滚动行为 :在创建路由时配置 scrollBehavior,实现跳转后回到顶部或记住位置。
js
const router = createRouter({
scrollBehavior(to, from, savedPosition) {
if (savedPosition) return savedPosition // 后退时保持原位置
else return { top: 0 } // 正常跳转滚回顶部
}
})
7. 路由懒加载的魔法注释与预加载
除了基础动态 import,还可以用 webpack 的魔术注释控制打包:
js
const User = () => import(/* webpackChunkName: "user-group" */ '../views/User.vue')
const UserPosts = () => import(/* webpackChunkName: "user-group" */ '../views/UserPosts.vue')
相同 ChunkName 的会被打进同一个文件,避免碎片化。还可以利用 <link rel="prefetch"> 做预加载。
8. 元信息的高级玩法:页面标题与权限
路由 meta 就是个筐,什么都能往里装。结合守卫,可以非常灵活。
js
// 路由配置
{ path: '/dashboard', meta: { title: '仪表盘', requiresAuth: true, roles: ['admin'] } }
// 全局后置守卫设置标题
router.afterEach(to => {
document.title = to.meta.title || '默认标题'
})
9. 路由别名与重定向
-
重定向:访问 A 跳转到 B,URL 会变。
js{ path: '/home', redirect: '/' } // 或函数形式 { path: '/old/:id', redirect: to => ({ path: '/new/' + to.params.id }) } -
别名:URL 保持为 A,但匹配 B 的组件,且 URL 显示 A。
js{ path: '/', component: Home, alias: '/homepage' }访问
/homepage时地址栏仍显示/homepage,但渲染Home组件。
10. 性能与调试
- 路由组件缓存
用
<keep-alive>包裹<router-view>,实现后退时保留状态。
- 避免不必要的导航
使用
router.push前检查当前路由,防止重复导航报错(或全局捕获NavigationDuplicated错误)。
- Vue Devtools
安装官方浏览器插件,可以直观看到路由状态、守卫触发顺序、组件树。
------个人观点 · 仅供参考------