一、導航守衛
?vue Router 中的 導航守衛(Navigation Guards) 是一個非常重要的功能,用于在路由切換過程中,攔截、控制、檢查或延遲頁面跳轉。
你可以理解為:
🔐 “進門前的保安”,控制哪些頁面可以進入,哪些要被攔下,甚至可以臨時讓你等一等(如加載數據)。
1、導航守衛是什么?
導航守衛 = 在頁面跳轉時運行的“鉤子函數”,用于:
-
登錄驗證(如沒登錄 → 攔住不讓進)
-
權限判斷(普通用戶不能進后臺)
-
加載數據(等異步數據加載完再跳轉)
-
保存草稿、彈窗確認(是否離開頁面)
2、導航過程中的“六大守衛鉤子”
1. 全局守衛(作用于整個路由系統)
全局守衛是指:在所有路由跳轉時都會觸發的攔截鉤子函數,你可以在這里:
-
檢查權限(如登錄驗證)
-
記錄日志
-
控制頁面跳轉流程
-
加載全局資源
-
攔截非法路徑、錯誤跳轉
1、 全局守衛的分類(3種)
守衛名稱 | 執行時機 | 是否需要調用 next() |
---|---|---|
beforeEach | 跳轉開始前 | ? 是 |
beforeResolve | 所有組件守衛解析后 | ? 是 |
afterEach | 跳轉結束后 | ? 否(只是通知) |
?1.最常用的:router.beforeEach()
router.beforeEach((to, from, next) => {// to: 即將進入的路由// from: 當前離開的路由// next(): 控制是否放行
})
?示例:登錄攔截
router.beforeEach((to, from, next) => {const isLogin = !!localStorage.getItem('token')if (to.meta.requiresAuth && !isLogin) {next('/login') // 沒登錄跳轉登錄頁} else {next() // 放行}
})
next()
的幾種用法
寫法 | 作用 |
---|---|
next() | 允許正常跳轉 |
next(false) | 中止導航(地址欄會回退) |
next('/login') | 重定向到其他頁面 |
next(new Error()) | 拋出錯誤,觸發全局錯誤捕獲 |
2.beforeResolve()
(較少使用)
router.beforeResolve((to, from, next) => {// 所有 beforeEach、組件 beforeRouteEnter、beforeEnter 都 resolve 后執行next()
})
用途:
-
等待所有數據加載完再顯示加載動畫
-
對所有守衛結果做最后決策
?3.afterEach()
(無 next)
router.afterEach((to, from) => {// 跳轉后做事情,如:sendAnalytics(to.path)
})
-
不能攔截跳轉,只是一個通知鉤子
-
用于記錄操作、埋點統計、結束 loading 等
2、執行順序圖解
如果你從 /login
跳到 /dashboard
:
?→ router.beforeEach()
→ 路由配置里的 beforeEnter(如果有)
→ 組件的 beforeRouteEnter(如果有)
→ router.beforeResolve()
→ 頁面渲染
→ router.afterEach()
3、實際應用場景
場景 | 使用鉤子 |
---|---|
登錄狀態攔截 | beforeEach |
動態加載權限路由 | beforeEach |
請求加載動畫 | beforeResolve |
頁面跳轉埋點統計 | afterEach |
首次訪問重定向 | beforeEach |
2、?路由獨享守衛(配置在某個路由項中)
1、什么是“路由獨享守衛”?
路由獨享守衛是定義在某一個路由配置項中的鉤子函數,只在進入該路由時觸發。
📌 區別于全局守衛,它不會影響其他頁面,適合只對特定路由做控制。
?語法格式
{path: '/admin',component: AdminPage,beforeEnter(to, from, next) {// 只會在進入 /admin 時觸發if (isAdmin()) {next() // 放行} else {next('/no-access') // 重定向}}
}
2、典型用途場景
場景 | 示例 |
---|---|
后臺路由權限攔截 | /admin 只有管理員能進 |
特定頁面加載前校驗數據 | /editor 頁面檢查草稿是否存在 |
臨時頁面的跳轉控制 | /event/:id 檢查活動是否開啟 |
3、和全局守衛的區別
點 | 全局守衛 | 路由獨享守衛 |
---|---|---|
觸發時機 | 所有路由跳轉都觸發 | 只在進入某個特定頁面時觸發 |
代碼位置 | 路由實例中 | 單個路由配置項中 |
使用場景 | 登錄檢查、全站控制 | 單頁權限、頁面特殊邏輯 |
示例:后臺權限驗證
const routes = [{path: '/admin',component: AdminView,beforeEnter(to, from, next) {const user = getUserInfo()if (user.role === 'admin') {next()} else {next('/403')}}}
]
4、注意事項
-
如果你既使用了全局守衛又用了路由獨享守衛,它們是串聯執行的。
-
路由獨享守衛在組件還沒創建時就執行,因此不能訪問組件實例
this
。
?3. 組件內守衛(定義在組件中)
1、什么是組件內守衛?
組件內守衛是直接定義在組件內部的路由鉤子函數,用于在“進入當前組件”、“更新當前組件參數”或“離開當前組件”時執行邏輯。
?它和全局守衛 / 路由獨享守衛相比,更適用于組件內部邏輯控制,如彈窗提示、數據加載、組件狀態管理等。
2、組件內三種守衛(使用時機)
守衛名 | 觸發時機 | 常見用途 |
---|---|---|
beforeRouteEnter | 即將進入組件前(組件還未創建) | 判斷是否允許進入、等數據加載完 |
beforeRouteUpdate | 同組件但路由參數變化時 | 響應式更新組件內容 |
beforeRouteLeave | 離開當前組件時 | 彈窗確認、保存草稿 |
示例講解
?1. beforeRouteEnter(to, from, next)
📌 組件還沒創建,所以你不能訪問 this
!
export default {beforeRouteEnter(to, from, next) {// 在進入該組件前執行console.log('準備進入這個組件')// 如果你需要訪問組件實例,可以這樣寫:next(vm => {console.log('組件實例是:', vm)})}
}
2. beforeRouteUpdate(to, from, next)
📌 組件已經渲染,只是參數變了
常用于如 /user/1
→ /user/2
的這種路徑變化
export default {beforeRouteUpdate(to, from, next) {// 當 :id 參數變化時觸發this.userId = to.params.idthis.fetchData()next()}
}
3.beforeRouteLeave(to, from, next)
📌 離開當前組件前觸發
常用于彈窗確認:
export default {beforeRouteLeave(to, from, next) {if (this.formChanged) {const answer = window.confirm('你有未保存的更改,確定離開?')if (!answer) return next(false)}next() // 放行}
}
3、注意事項
-
beforeRouteEnter
無法訪問this
,必須用next(vm => {...})
才能訪問組件實例。 -
如果你使用的是
<script setup>
,這些守衛要通過onBeforeRouteLeave()
等組合式 API 來注冊。
3、守衛執行順序圖解
假設你從 /a
→ /b
,執行順序是:
?1. 全局 beforeEach
2. 路由 beforeEnter
3. 組件 beforeRouteLeave(離開 a)
4. 組件 beforeRouteEnter(進入 b)
5. 全局 beforeResolve
6. 頁面渲染
7. 全局 afterEach
4、常見坑點
錯誤用法 | 正確說明 |
---|---|
next() 忘記寫 | 會導致跳轉永遠卡住 |
beforeRouteEnter 中訪問 this | 不行!因為組件還沒被創建,用 next(vm => {...}) |
beforeRouteUpdate 不寫 | 跳轉到相同組件(比如 user/1 → user/2 )時,頁面不會更新 |
異步未處理錯誤 | 加 try/catch,或者重定向到錯誤頁 |
?二、路由元信息
Vue Router 中的路由元信息(meta
)是一個非常強大但簡單的功能,常用于在路由配置中添加額外的自定義數據,比如:
-
是否需要登錄權限
-
當前頁面的標題
-
頁面緩存控制
-
菜單顯示圖標
-
動態路由權限判斷
1、什么是路由元信息?
meta
是路由對象中的一個可選字段,用于存儲自定義的附加信息,Vue Router 不會主動使用,但你可以在導航守衛、組件中等地方訪問并利用它。
語法結構示例
{path: '/admin',component: AdminView,meta: {requiresAuth: true,title: '管理后臺',icon: 'admin-icon',cache: true}
}
這段配置的含義就是:
-
訪問
/admin
時,需要登錄 -
頁面標題是“管理后臺”
-
菜單顯示圖標為 admin-icon
-
頁面可以緩存
2、 怎么訪問 meta?
你可以通過 route.meta
來訪問元信息:?
1. 在全局導航守衛中:
router.beforeEach((to, from, next) => {if (to.meta.requiresAuth && !isLoggedIn()) {next('/login')} else {next()}
})2. 在組件中訪問:
import { useRoute } from 'vue-router'
const route = useRoute()
console.log(route.meta.title) // 頁面標題
3、典型應用場景(非常實用)
應用場景 | 用法示例 |
---|---|
登錄權限控制 | meta: { requiresAuth: true } |
頁面標題設置 | meta: { title: '首頁' } |
動態菜單渲染 | meta: { icon: 'home', hidden: false } |
緩存控制 | meta: { cache: true } 配合 keep-alive |
用戶角色權限 | meta: { roles: ['admin', 'editor'] } |
是否展示標簽頁 | meta: { tag: true } (多標簽頁路由) |
4、注意事項
項目 | 說明 |
---|---|
meta 是用戶自定義的 | Vue Router 不會解釋它,你自己怎么用都行 |
子路由也可以有 meta | 如果沒有,你可以繼承父路由的 meta |
需要在組件或守衛中訪問 route 對象 | 使用 useRoute() 獲取當前路由信息 |
meta
是 Vue Router 路由配置中的“萬能自定義字段”,你可以往里面塞權限標記、頁面標題、緩存控制、菜單信息等任意邏輯,再通過路由守衛或組件動態處理。?
三、?<RouterView>
插槽
RouterView
是 Vue Router 提供的內置組件,專門用來渲染“命中的子路由組件”。
而它的插槽(slot)機制,讓我們可以更靈活地控制它所渲染的內容。
從 Vue Router 4 開始,<RouterView>
支持默認插槽和作用域插槽。
1、使用插槽做什么?
通過插槽你可以:
用途 | 說明 |
---|---|
? 頁面過渡動畫 | 為切換的路由組件加動畫 |
🌀 加載指示器 | 在組件未加載前顯示 loading |
🎯 動態包裝 | 自定義如何渲染子組件,例如加布局、卡片框等 |
2、 插槽語法
<RouterView v-slot="{ Component, route }"><!-- 自定義展示方式 -->
</RouterView>變量 說明
Component 當前要渲染的子路由組件
route 當前激活的路由對象(等同于 useRoute())
?示例:為路由組件加過渡動畫
<RouterView v-slot="{ Component }"><Transition name="fade"><component :is="Component" /></Transition>
</RouterView>
📌 解釋:你接管了 RouterView 默認渲染行為自己通過 <component :is="Component" /> 渲染它可插入 <Transition> 包裹來加動畫
3、使用場景總結
場景 | 解決問題 |
---|---|
組件加載前展示 loading | 配合 <Suspense> |
切換頁面時添加過渡動畫 | 用 <Transition> |
自定義布局結構包裝組件 | 外層加卡片/容器等 |
統一處理錯誤提示 | 在外層判斷并渲染 fallba |
?三、路由懶加載
路由懶加載(Route Lazy Loading) 是 Vue Router 中的一個優化技巧,主要目的是:
? 將路由組件按需加載,而不是一次性全部加載,從而提升首頁加載速度、減少初始包體積、加快頁面響應。
1、什么是“懶加載”?
懶加載(Lazy Load)就是在用戶訪問某個路由時才去加載對應的組件代碼。
不是在一開始加載整個應用時就加載所有路由頁面,而是需要時再加載、用完即用。
2、為什么需要懶加載?
不懶加載時會發生什么?
-
項目中所有頁面組件在第一次打開應用時全部加載
-
首屏加載時間變慢(特別是頁面很多時)
-
用戶可能只訪問其中某幾個頁面,卻加載了所有組件
?3、懶加載的實現方式
傳統方式(同步加載)
import Home from '@/views/Home.vue'const routes = [{ path: '/', component: Home }
]缺點:Home.vue 會在頁面初始化時就被加載
?推薦方式:使用 import()
動態導入
const Home = () => import('@/views/Home.vue')const routes = [{ path: '/', component: Home }
]
這個語法的核心:
-
import()
返回一個 Promise -
Vue 會把這個組件單獨打包成一個 懶加載 chunk
-
當你訪問
/
時,它才會發起網絡請求下載該頁面代碼
4、懶加載適合什么項目?
項目類型 | 是否建議使用懶加載 |
---|---|
小型 SPA,頁面少 | 不強制,可選 |
多頁面、功能復雜 | ? 強烈建議懶加載 |
管理系統、CMS、商城 | ? 一定要懶加載 |
首屏要極快響應 | ? 可只加載核心頁面,其它懶加載 |
?四、動態路由
1、什么是動態路由?
動態路由是指:你可以在路由路徑中定義“參數變量”,從而匹配不同的 URL,渲染相同的組件但顯示不同的內容。
基礎語法
?路由配置:
{path: '/user/:id',component: UserDetail
}
:id 是一個動態路徑參數訪問 /user/1、/user/2 會匹配同一個路由,但參數不同
在組件中獲取動態參數
?在 <script setup>
中:
import { useRoute } from 'vue-router'const route = useRoute()
console.log(route.params.id) // 輸出當前的用戶 ID
你可以用它去請求對應用戶的數據:
const res = await fetch(`/api/user/${route.params.id}`)
動態參數更新時自動響應
問題:
訪問 /user/1
后,再跳轉 /user/2
,組件不會重新創建,這時你要用:
import { onBeforeRouteUpdate, useRoute } from 'vue-router'const route = useRoute()onBeforeRouteUpdate((to, from, next) => {// 監聽參數變化fetchData(to.params.id)next()
})
?或者使用 <watch>
監聽 route 變化。
props 配合動態參數(推薦做法)
路由配置:
{path: '/user/:id',component: UserDetail,props: true
}
? 組件中直接用 props 接收參數:<script setup>
const props = defineProps(['id'])
</script>
這樣你無需手動解析 useRoute(),更干凈、易測試。
2、什么是動態路由添加?
通俗解釋:
動態路由添加就是:在運行時動態注冊新的路由,而不是一開始就寫死在
routes
里。
常見于以下場景:
-
登錄后根據權限動態添加路由
-
加載某個模塊時再注冊其頁面路由
-
插件/微前端中按需注冊子應用路由
?動態添加路由的基本方法:router.addRoute()
router.addRoute({ path: '/about', component: About })
這樣會將 /about 的路由 添加進當前路由系統中。
?🧠 但注意:
如果你當前已經在 /about 頁面上了,它會繼續顯示之前的匹配結果,比如 /:articleName,因為 Vue Router 只在切換路由時重新匹配!
解決辦法:
// 添加新路由
router.addRoute({ path: '/about', component: About })
// 替換當前路徑以強制重新匹配
router.replace(router.currentRoute.value.fullPath)這樣 Vue Router 才會重新匹配新添加的 /about 路由,并正確渲染 About 組件。
3、在導航守衛中動態添加路由
動態加載路由的同時判斷是否已有該路由,并觸發重定向。
router.beforeEach(to => {if (!hasNecessaryRoute(to)) {router.addRoute(generateRoute(to)) // 添加路由return to.fullPath // 觸發重定向}
})
📌 解釋:
-
hasNecessaryRoute()
:判斷當前要訪問的路由是否已經注冊 -
如果沒有 → 添加 → 返回
to.fullPath
觸發跳轉(類似next(to.fullPath)
)
🔁 必須小心處理遞歸添加/跳轉,以防無限重定向
4、如何刪除路由?
? 方式一:名字相同會覆蓋
router.addRoute({ name: 'about', path: '/about', component: About })
router.addRoute({ name: 'about', path: '/other', component: Other })
// 原來的 'about' 路由會被刪除,新的替代
方式二:使用 addRoute()
返回的函數
const remove = router.addRoute({ path: '/settings', component: Settings })
remove() // 取消注冊該路由
適合用于組件掛載時注冊、卸載時移除。
?方式三:顯式調用 router.removeRoute(name)
router.removeRoute('about')
?? 只能刪除命名路由
?5、動態添加嵌套路由(children)
// 添加父路由
router.addRoute({ name: 'admin', path: '/admin', component: Admin })
// 向 admin 路由添加子路由
router.addRoute('admin', { path: 'settings', component: AdminSettings })
等價于:
{name: 'admin',path: '/admin',component: Admin,children: [{ path: 'settings', component: AdminSettings }]
}
動態嵌套路由常用于:
-
模塊懶加載時
-
子模塊按需注冊
6、查看和判斷當前路由表
Vue Router 提供兩個方法:
方法 | 功能 |
---|---|
router.hasRoute(name) | 判斷某個命名路由是否存在 |
router.getRoutes() | 獲取所有當前注冊的路由(包括 |
示例
// 添加
if (!router.hasRoute('dashboard')) {router.addRoute({ name: 'dashboard', path: '/dashboard', component: Dashboard })
}// 刪除
if (router.hasRoute('dashboard')) {router.removeRoute('dashboard')
}
7、總結
動態路由讓你可以在運行時控制哪些頁面存在、哪些功能可訪問,是構建權限控制、模塊懶加載、大型系統菜單的關鍵機制。配合
addRoute()
、removeRoute()
、router.replace()
使用可以靈活操作整個路由系統。