前言
Vue Router 是 Vue 生態中處理頁面跳轉的核心工具,它解決了單頁應用中 URL 管理、組件切換、狀態維護等關鍵問題,同時提供了豐富的功能(如動態路由、嵌套路由、路由守衛)。除了經常用到的路由配置以外,我們還需了解以下幾點。
1. Vue 路由模式
Vue Router 支持兩種路由模式:
模式 | 原理 | 特點 | 啟用方式 |
---|---|---|---|
Hash 模式 | 使用 URL hash (# ) 模擬完整 URL http://example.com/#/home | 兼容性好(支持 IE9) 無需服務器配置 | mode: 'hash' (默認) |
History 模式 | 基于 HTML5 History API http://example.com/home | URL 更美觀 需要服務器端支持 | mode: 'history' |
服務器配置示例 (History 模式):
# Nginx 配置
location / {try_files $uri $uri/ /index.html;
}
2. 路由懶加載
實現組件按需加載,提升首屏性能。
實現方式:
const router = new VueRouter({routes: [// 魔法注釋:webpackChunkName 定義分包名稱{ path: '/dashboard',component: () => import(/* webpackChunkName: "dashboard"*/'./views/Dashboard.vue')},{path: '/user/:id',component: () => import(/* webpackChunkName: "user" */ './views/UserProfile.vue')}]
})
優化效果:
- 首屏加載時間減少 30-50%
- 按路由拆分 chunk 文件
- 支持預加載:
<link rel="prefetch">
3. 路由緩存
使用 <keep-alive>
緩存組件狀態:
<template><div id="app"><!-- 緩存帶有 meta.keepAlive 的路由 --><keep-alive :include="cachedViews"><router-view v-if="$route.meta.keepAlive"></router-view></keep-alive><!-- 不緩存的路由 --><router-view v-if="!$route.meta.keepAlive"></router-view></div>
</template><script>
export default {computed: {cachedViews() {return this.$store.state.cachedRoutes}}
}
</script>
路由配置:
{path: '/user/:id',component: UserDetails,meta: { keepAlive: true, // 啟用緩存scrollPos: true // 記錄滾動位置}
}
可通過屬性控制緩存范圍:
include
:僅緩存名稱匹配的組件(組件需定義name
屬性)。exclude
:不緩存名稱匹配的組件。max
:最多緩存的組件實例數量。
示例:
<keep-alive include="Home,About" :max="10"><router-view />
</keep-alive>
被緩存的組件會觸發 activated
(激活時)和 deactivated
(失活時)生命周期鉤子,替代 mounted
和 unmounted
。
4. 路由守衛
守衛類型:
// 1. 全局前置守衛
router.beforeEach((to, from, next) => {if (to.meta.requiresAuth && !isLoggedIn()) next('/login')else next()
})// 2. 路由獨享守衛
{path: '/admin',beforeEnter: (to, from, next) => {checkAdminPermission() ? next() : next('/403')}
}// 3. 組件內守衛
const UserProfile = {beforeRouteEnter(to, from, next) {// 不能訪問 thisnext(vm => console.log(vm.user)) // 通過回調訪問組件實例},beforeRouteUpdate(to, from, next) {// 路由參數變化時觸發this.fetchData(to.params.id)next()},beforeRouteLeave(to, from, next) {// 離開確認window.onbeforeunload = nullnext()}
}
1.全局路由
Route.beforeEeach 任意路由跳轉前觸發
Route.beforeResolve 導航確認前觸發
Route.afterEach 導航完成后觸發
2.路由獨享守衛
beforeEnter 在路由配置上定義,進入路由時觸發
3.組件內守衛
beforeRouteEnter 渲染組件路由被驗證前調用(適合做進入前的權限驗證,不能訪問this)
beforeRouteUpdate 當組件路由改變,組件復用時候調用(適合在路由參數變化時重新加載數據
beforeRouteLeave 導航離開組件的對應路由時調用(適合做離開前的確認(如未保存的表單)
導航解析流程:
- 1.觸發導航 → 生成目標路由
- 2.在失活的組件里調用(
beforeRouteLeave
) - 3.執行 全局前置守衛(
beforeEach
) - 4.在重用的組件里調用beforeRouteUpdate
- 5.執行 路由獨享守衛(
beforeEnter
) - 6.執行 組件內前置守衛(
beforeRouteEnter
) - 7.解析異步路由組件:如
() => import('./User.vue')
- 8.執行 組件內更新守衛(
beforeRouteUpdate
,如適用) - 9.執行 組件內離開守衛(
beforeRouteLeave
,如適用) - 10.執行 全局解析守衛(
beforeResolve
) - 11.確認導航,更新 URL 和路由記錄
- 12.執行 全局后置鉤子(
afterEach
) - 13.Dom更新:銷毀舊組件,創建并渲染新組件 → 完成導航
通過這個流程,Vue Router 實現了對路由導航的精細控制,結合不同類型的守衛可以滿足權限驗證、數據預加載、離開確認等多種業務需求。
5. 路由與后端菜單結合
動態路由工作流:
實現代碼:
// 1. 定義基礎路由(無需權限)
const constantRoutes = [{ path: '/login', component: Login },{ path: '/404', component: NotFound }
]// 2. 從后端獲取菜單
async function initRoutes() {const menuData = await axios.get('/api/user/menus')const dynamicRoutes = generateRoutes(menuData)// 3. 動態添加路由dynamicRoutes.forEach(route => router.addRoute(route))// 4. 添加404捕獲router.addRoute({ path: '*', redirect: '/404' })
}// 菜單轉換函數
function generateRoutes(menuData) {return menuData.map(menu => ({path: menu.path,component: () => import(`@/views/${menu.component}`),meta: { title: menu.title, icon: menu.icon },children: menu.children ? generateRoutes(menu.children) : []}))
}
6. 路由權限控制
完整權限方案:
// 權限檢查函數
function hasPermission(route, roles) {if (route.meta?.roles) {return roles.some(role => route.meta.roles.includes(role))}return true // 無meta.roles則公開訪問
}// 路由過濾
function filterRoutes(routes, roles) {return routes.filter(route => {if (hasPermission(route, roles)) {if (route.children) {route.children = filterRoutes(route.children, roles)}return true}return false})
}// 全局前置守衛
router.beforeEach(async (to, from, next) => {// 1. 獲取用戶角色const roles = store.getters.roles || []// 2. 白名單檢查if (whiteList.includes(to.path)) return next()// 3. 未登錄重定向if (!roles.length) return next(`/login?redirect=${to.path}`)// 4. 已登錄但未初始化路由if (!store.getters.routesLoaded) {await initDynamicRoutes(roles) // 動態添加路由return next({ ...to, replace: true })}// 5. 檢查目標路由權限if (!hasPermission(to, roles)) return next('/403')next()
})
按鈕級權限控制:
<template><button v-permission="'user:create'">添加用戶</button>
</template>
// 權限指令
Vue.directive('permission', {inserted(el, binding) {const { value } = bindingconst permissions = store.getters.permissionsif (value && !permissions.includes(value)) {el.parentNode?.removeChild(el)}}
})
最佳實踐總結
- 路由設計:
- 公共路由使用靜態注冊
- 權限路由使用動態注冊
- 路由元信息存儲權限標識
- 性能優化:
- 路由懶加載 + webpack 分包
- 滾動行為恢復
- 路由組件復用
- 安全控制:
- 前端路由權限校驗
- 后端接口二次驗證
- 按鈕級權限控制
- 錯誤處理:
- 統一404處理
- 403無權限頁面
- 路由切換錯誤捕獲
// 錯誤捕獲
router.onError(error => {console.error('路由錯誤:', error)if (/ChunkLoadError/.test(error.message)) {window.location.reload() // 重新加載解決chunk加載失敗}
})