Vue路由深度解析:Vue Router與導航守衛
一、Vue Router基礎與安裝配置
1. Vue Router核心概念
Vue Router是Vue.js官方的路由管理器,主要功能包括:
- 嵌套路由映射
- 模塊化的路由配置
- 路由參數、查詢、通配符
- 細粒度的導航控制
- 自動激活的CSS類鏈接
- HTML5 history模式或hash模式
- 可定制的滾動行為
2. 安裝與基本配置
安裝Vue Router:
npm install vue-router@4
# 或
yarn add vue-router@4
基礎配置示例:
// router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'const routes = [{path: '/',name: 'home',component: HomeView},{path: '/about',name: 'about',// 路由級代碼拆分component: () => import('../views/AboutView.vue')}
]const router = createRouter({history: createWebHistory(process.env.BASE_URL),routes
})export default router
在main.js中引入:
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'const app = createApp(App)
app.use(router)
app.mount('#app')
二、路由模式與路由配置詳解
1. 路由模式對比
模式 | 特點 | 示例 |
---|---|---|
Hash模式 | 使用URL hash模擬完整URL | http://example.com/#/about |
History模式 | 使用HTML5 History API | http://example.com/about |
Memory模式 | 不修改URL,適合非瀏覽器環境(如Electron) | 無URL變化 |
配置示例:
import { createWebHashHistory, // Hash模式createWebHistory, // History模式createMemoryHistory // Memory模式
} from 'vue-router'const router = createRouter({history: createWebHistory(), // 推薦生產環境使用// history: createWebHashHistory(), // 兼容性更好// history: createMemoryHistory(), // 非瀏覽器環境routes
})
2. 動態路由與參數傳遞
動態路由配置:
const routes = [// 動態字段以冒號開始{path: '/user/:id',name: 'user',component: UserView},// 可匹配/user/123或/user/456{path: '/user/:id/posts/:postId',component: UserPostView}
]
參數獲取方式:
<template><!-- 在模板中直接使用 --><div>用戶ID: {{ $route.params.id }}</div>
</template><script setup>
import { useRoute } from 'vue-router'// 在setup中使用
const route = useRoute()
console.log(route.params.id)
</script><script>
// 在Options API中使用
export default {created() {console.log(this.$route.params.id)}
}
</script>
3. 嵌套路由與命名視圖
嵌套路由配置:
const routes = [{path: '/user/:id',component: UserLayout,children: [{path: '', // 默認子路由component: UserProfile},{path: 'posts',component: UserPosts},{path: 'settings',component: UserSettings}]}
]
命名視圖(多路由出口):
const routes = [{path: '/',components: {default: HomeView, // 默認出口sidebar: SidebarView, // <router-view name="sidebar">footer: AppFooter // <router-view name="footer">}}
]
三、導航守衛全面解析
1. 導航守衛類型與執行流程
完整的導航解析流程:
- 導航被觸發
- 調用
beforeRouteLeave
守衛(組件內) - 調用全局
beforeEach
守衛 - 在重用的組件里調用
beforeRouteUpdate
守衛(組件內) - 調用路由配置里的
beforeEnter
守衛 - 解析異步路由組件
- 在被激活的組件里調用
beforeRouteEnter
(組件內) - 調用全局
beforeResolve
守衛 - 導航被確認
- 調用全局
afterEach
鉤子 - 觸發DOM更新
- 調用
beforeRouteEnter
守衛中傳給next
的回調函數
2. 全局守衛
// router/index.js// 全局前置守衛
router.beforeEach((to, from, next) => {console.log('全局前置守衛', to, from)// 必須調用next()繼續導航next()
})// 全局解析守衛
router.beforeResolve((to, from, next) => {console.log('全局解析守衛', to, from)next()
})// 全局后置鉤子
router.afterEach((to, from) => {console.log('全局后置鉤子', to, from)// 不需要next函數
})
3. 路由獨享守衛
const routes = [{path: '/admin',component: AdminView,beforeEnter: (to, from, next) => {// 僅在此路由觸發if (isAdmin()) next()else next('/login')}}
]
4. 組件內守衛
<script>
export default {beforeRouteEnter(to, from, next) {// 在渲染該組件的對應路由被驗證前調用// 不能獲取組件實例 `this`next(vm => {// 通過 `vm` 訪問組件實例console.log(vm.someData)})},beforeRouteUpdate(to, from) {// 當前路由改變,但是該組件被復用時調用// 可以訪問組件實例 `this`this.fetchData(to.params.id)},beforeRouteLeave(to, from) {// 導航離開該組件的對應路由時調用// 可以訪問組件實例 `this`const answer = window.confirm('確定要離開嗎?未保存的更改將會丟失')if (!answer) return false}
}
</script>
5. 導航守衛實戰:權限控制
// router/index.js
import { useAuthStore } from '@/stores/auth'const routes = [{path: '/',name: 'home',component: HomeView,meta: { requiresAuth: false }},{path: '/dashboard',name: 'dashboard',component: DashboardView,meta: { requiresAuth: true,roles: ['admin', 'editor'] }},{path: '/admin',name: 'admin',component: AdminView,meta: { requiresAuth: true,roles: ['admin'] }}
]router.beforeEach(async (to, from, next) => {const authStore = useAuthStore()const isAuthenticated = authStore.isAuthenticatedconst userRole = authStore.user?.role || 'guest'// 檢查路由是否需要認證if (to.meta.requiresAuth && !isAuthenticated) {return next({ name: 'login', query: { redirect: to.fullPath } })}// 檢查路由角色權限if (to.meta.roles && !to.meta.roles.includes(userRole)) {return next({ name: 'forbidden' })}// 如果用戶已登錄但要去登錄頁,重定向到首頁if (to.name === 'login' && isAuthenticated) {return next({ name: 'home' })}next()
})
四、路由高級特性
1. 路由元信息與過渡動畫
路由元信息配置:
const routes = [{path: '/posts',component: PostsLayout,meta: { requiresAuth: true,transition: 'slide-left' },children: [{path: 'new',component: NewPost,meta: { transition: 'slide-up',requiresAdmin: true }}]}
]
動態過渡效果:
<template><router-view v-slot="{ Component, route }"><transition :name="route.meta.transition || 'fade'"><component :is="Component" /></transition></router-view>
</template><style>
.fade-enter-active,
.fade-leave-active {transition: opacity 0.3s ease;
}.fade-enter-from,
.fade-leave-to {opacity: 0;
}.slide-left-enter-active,
.slide-left-leave-active {transition: transform 0.3s ease;
}.slide-left-enter-from {transform: translateX(100%);
}.slide-left-leave-to {transform: translateX(-100%);
}.slide-up-enter-active,
.slide-up-leave-active {transition: transform 0.3s ease;
}.slide-up-enter-from {transform: translateY(100%);
}.slide-up-leave-to {transform: translateY(-100%);
}
</style>
2. 滾動行為控制
const router = createRouter({history: createWebHistory(),routes,scrollBehavior(to, from, savedPosition) {// 返回滾動位置對象if (savedPosition) {return savedPosition // 瀏覽器前進/后退時恢復位置}if (to.hash) {return {el: to.hash, // 滾動到錨點behavior: 'smooth' // 平滑滾動}}if (to.meta.scrollToTop) {return { top: 0 } // 新路由滾動到頂部}// 默認不改變滾動位置}
})
3. 路由懶加載與分包
基礎懶加載:
const routes = [{path: '/about',component: () => import('../views/AboutView.vue')}
]
自定義分包:
const routes = [{path: '/admin',component: () => import(/* webpackChunkName: "admin" */ '../views/AdminView.vue'),children: [{path: 'dashboard',component: () => import(/* webpackChunkName: "admin" */ '../views/AdminDashboard.vue')}]},{path: '/user/:id',component: () => import(/* webpackChunkName: "user" */ '../views/UserView.vue')}
]
4. 動態路由API
添加路由:
router.addRoute({path: '/new-route',name: 'newRoute',component: () => import('../views/NewView.vue')
})// 添加到現有路由的子路由
router.addRoute('parentRoute', {path: 'child',component: () => import('../views/ChildView.vue')
})
刪除路由:
// 通過名稱刪除
router.removeRoute('routeName')// 通過添加返回的回調刪除
const removeRoute = router.addRoute(routeConfig)
removeRoute() // 刪除路由
檢查路由:
// 檢查路由是否存在
router.hasRoute('routeName')// 獲取所有路由記錄
router.getRoutes()
五、常見問題與最佳實踐
1. 常見問題解決方案
問題1:路由重復跳轉報錯
// 統一處理導航錯誤
router.onError((error) => {if (error.message.includes('Avoided redundant navigation')) {// 忽略重復導航錯誤} else {// 處理其他導航錯誤}
})
問題2:動態路由刷新404
- History模式需要服務器配置支持
- Nginx配置示例:
location / {try_files $uri $uri/ /index.html; }
問題3:路由組件不更新
// 使用beforeRouteUpdate或監聽$route
watch(() => route.params.id,(newId) => {fetchData(newId)}
)
2. 最佳實踐建議
-
路由組織:
- 按功能模塊組織路由文件
- 使用路由元信息(meta)存儲權限、標題等信息
- 對大型項目考慮自動導入路由
-
性能優化:
- 合理使用路由懶加載
- 對頻繁訪問的路由考慮預加載
- 避免在導航守衛中進行繁重操作
-
安全實踐:
- 始終驗證前端路由權限
- 敏感路由應在后端再次驗證
- 使用路由獨享守衛處理特殊權限
-
開發體驗:
- 為路由添加name屬性方便跳轉
- 使用路由元信息管理頁面標題
- 實現進度條提升用戶體驗
六、綜合實戰:企業級路由方案
1. 完整路由配置示例
// router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import { useAuthStore } from '@/stores/auth'
import NProgress from 'nprogress'const routes = [{path: '/',name: 'home',component: () => import('@/views/HomeView.vue'),meta: {title: '首頁',requiresAuth: false,cache: true}},{path: '/login',name: 'login',component: () => import('@/views/LoginView.vue'),meta: {title: '登錄',guestOnly: true}},{path: '/dashboard',name: 'dashboard',component: () => import('@/views/DashboardView.vue'),meta: {title: '儀表盤',requiresAuth: true}},{path: '/admin',name: 'admin',component: () => import('@/views/layouts/AdminLayout.vue'),meta: {title: '管理后臺',requiresAuth: true,roles: ['admin']},children: [{path: '',name: 'admin-dashboard',component: () => import('@/views/admin/DashboardView.vue'),meta: { title: '控制臺' }},{path: 'users',name: 'admin-users',component: () => import('@/views/admin/UsersView.vue'),meta: { title: '用戶管理' }}]},{path: '/:pathMatch(.*)*',name: 'not-found',component: () => import('@/views/NotFoundView.vue'),meta: {title: '頁面不存在'}}
]const router = createRouter({history: createWebHistory(import.meta.env.BASE_URL),routes,scrollBehavior(to, from, savedPosition) {if (savedPosition) return savedPositionif (to.hash) return { el: to.hash, behavior: 'smooth' }return { top: 0 }}
})// 進度條配置
NProgress.configure({ showSpinner: false })// 全局前置守衛
router.beforeEach(async (to, from, next) => {NProgress.start()const authStore = useAuthStore()const isAuthenticated = authStore.isAuthenticatedconst userRole = authStore.user?.role || 'guest'// 設置頁面標題document.title = to.meta.title ? `${to.meta.title} | 我的應用` : '我的應用'// 檢查認證if (to.meta.requiresAuth && !isAuthenticated) {return next({name: 'login',query: { redirect: to.fullPath }})}// 檢查角色權限if (to.meta.roles && !to.meta.roles.includes(userRole)) {return next({ name: 'forbidden' })}// 已登錄用戶訪問guestOnly路由if (to.meta.guestOnly && isAuthenticated) {return next({ name: 'home' })}next()
})// 全局后置鉤子
router.afterEach(() => {NProgress.done()
})export default router
2. 路由工具函數
// utils/router.js
export function resetRouter() {const newRouter = createRouter()router.matcher = newRouter.matcher // 重置路由
}export function loadRoutesByRole(role) {const dynamicRoutes = []if (role === 'admin') {dynamicRoutes.push({path: '/admin',component: () => import('@/views/AdminView.vue'),children: [// 管理員專屬路由]})}dynamicRoutes.forEach(route => {router.addRoute(route)})
}export function getRouteTitle(route) {return route.meta.title || ''
}
3. 路由與狀態管理集成
// stores/app.js
import { defineStore } from 'pinia'
import { useRouter } from 'vue-router'export const useAppStore = defineStore('app', {state: () => ({cachedViews: [],visitedViews: []}),actions: {addCachedView(view) {if (this.cachedViews.includes(view.name)) returnif (view.meta?.cache) {this.cachedViews.push(view.name)}},addVisitedView(view) {const existing = this.visitedViews.find(v => v.path === view.path)if (existing) {if (existing.fullPath !== view.fullPath) {// 更新現有記錄Object.assign(existing, view)}return}this.visitedViews.push(Object.assign({}, view, {title: view.meta?.title || '未知'}))},async logout() {const router = useRouter()// 清理狀態this.$reset()// 重定向到登錄頁await router.push('/login')}}
})
通過本指南,您已經全面掌握了Vue Router的核心概念和高級用法。從基礎配置到導航守衛,從動態路由到狀態集成,這些知識將幫助您構建復雜且高效的單頁應用程序。實際項目中應根據具體需求選擇合適的路由方案,并遵循最佳實踐以確保應用的性能和可維護性。