一、Vue Router 基礎概念與核心原理
1.1 路由本質與核心要素
- 本質定義:路由是URL路徑與頁面組件的對應關系,通過路徑變化控制視圖切換,實現單頁應用(SPA)的無刷新頁面切換。
- 核心三要素:
router-link
:聲明式導航組件,替代原生<a>
標簽(避免頁面刷新),通過to
屬性指定目標路徑。router-view
:路由視圖出口,匹配路徑的組件會渲染到該標簽位置(路由有幾級嵌套,就需要幾個router-view
)。- 路由實例:通過
new VueRouter()
創建,配置路徑與組件的映射規則(routes
數組)。
1.2 路由模式對比(hash vs history)
路由模式決定URL的表現形式和底層實現邏輯,是面試高頻考點,需重點區分:
對比維度 | hash模式(默認) | history模式(推薦) |
---|---|---|
URL表現 | 帶# 號(如http://localhost:8080/#/home ) | 無# 號(如http://localhost:8080/home ) |
實現原理 | 基于window.onhashchange 事件監聽# 后路徑變化 | 基于HTML5 History API (pushState /popState ) |
后端依賴 | 純前端實現,# 后路徑不發送到后端,無需配置 | 路徑會完整發送到后端,刷新可能觸發404 |
404問題解決 | 無此問題 | 需后端配置(如Nginx):未匹配路徑返回index.html |
適用場景 | 簡單Demo、無需美觀URL的項目 | 生產環境、對URL美觀度和SEO有要求的項目 |
后端Nginx配置示例(解決history模式404)
location / {root /usr/share/nginx/html; # 項目打包后的目錄index index.html index.htm;try_files $uri $uri/ /index.html; # 核心配置:未匹配路徑返回index.html
}
1.3 路由注冊機制
Vue.use(VueRouter)
的必要性:
Vue Router是Vue生態專屬插件,需通過Vue.use()
顯式注冊,否則路由功能無法生效(類比“插座與插頭”,必須配套使用)。
注意:通用工具庫(如Axios)無需此步驟,僅Vue專屬插件(Vue Router、Vuex)需注冊。- 注冊原理:
Vue.use()
會調用插件內部的install
方法,將路由實例注入Vue全局,使所有組件可通過this.$router
訪問路由實例。
二、路由核心配置流程(Vue 2 + Vue Router 3.x)
路由配置需遵循“模塊化”原則,避免與main.js
混雜,標準流程如下:
2.1 標準配置步驟(五步走)
步驟1:創建路由目錄與文件
在src
下新建router
文件夾,創建index.js
(路由配置主文件,Webpack默認優先查找index.js
)。
步驟2:引入依賴并注冊插件
// src/router/index.js
import Vue from 'vue' // 引入Vue核心
import VueRouter from 'vue-router' // 引入Vue Router
Vue.use(VueRouter) // 注冊路由插件(必須步驟)
步驟3:定義路由規則(routes
數組)
routes
是路由配置的核心,每個路由對象包含path
(路徑)、component
(對應組件)等屬性:
// 1. 引入組件(支持懶加載,優化首屏性能)
const Home = () => import('@/views/Home.vue') // 懶加載寫法
const About = () => import('@/views/About.vue')
const User = () => import('@/views/User.vue')// 2. 定義路由規則
const routes = [{path: '/', // 默認路徑redirect: '/home' // 重定向到首頁(避免空白頁面)},{path: '/home',name: 'Home', // 路由名稱(可選,用于命名路由跳轉)component: Home},{path: '/about',name: 'About',component: About},{path: '/user/:id', // 動態路由::id為動態參數name: 'User',component: User}
]
步驟4:創建路由實例并導出
const router = new VueRouter({mode: 'history', // 路由模式:hash/history(默認hash)routes // 注入路由規則(ES6簡寫,等同于routes: routes)
})export default router // 導出路由實例,供main.js引入
步驟5:注入根實例并添加router-view
- 在
main.js
中引入路由實例:
// src/main.js
import Vue from 'vue'
import App from './App.vue'
import router from './router' // 引入路由實例(自動找router/index.js)new Vue({router, // 注入路由實例(使所有組件可訪問$router/$route)render: h => h(App)
}).$mount('#app')
- 在
App.vue
中添加router-view
(指定組件渲染位置):
<!-- src/App.vue -->
<template><div id="app"><!-- 1. 路由導航 --><nav><router-link to="/home">首頁</router-link><router-link to="/about">關于我們</router-link></nav><!-- 2. 路由視圖出口:匹配的組件會渲染到這里 --><router-view></router-view></div>
</template>
2.2 routes
數組核心屬性詳解
屬性 | 作用與說明 | 示例 |
---|---|---|
path | 路由路徑(必須以/ 開頭,子路由可省略/ 自動拼接父路徑) | /home (一級路由)、child (子路由) |
component | 路徑對應的組件(支持懶加載() => import() 和直接引入) | Home 、() => import('@/views/Home.vue') |
name | 路由名稱(唯一,用于命名路由跳轉,解耦路徑變化) | name: 'Home' |
children | 子路由配置數組(實現嵌套路由,每個子路由結構與父路由一致) | children: [{ path: 'child', component: Child }] |
redirect | 重定向(訪問當前路徑時自動跳轉到目標路徑,支持字符串/name對象) | redirect: '/home' 、redirect: { name: 'Home' } |
alias | 路由別名(給路由多個訪問路徑,地址欄顯示別名,組件不變) | alias: '/index' (訪問/index 等同于/home ) |
三、路由進階特性(實戰必備)
3.1 嵌套路由(多級路由)
嵌套路由用于實現“父組件包含子組件”的層級結構(如后臺管理系統的“側邊欄+內容區”),核心是children
配置和多級router-view
。
1. 配置示例(后臺管理系統)
// src/router/index.js
const Layout = () => import('@/views/Layout.vue') // 父布局組件
const Dashboard = () => import('@/views/Dashboard.vue') // 子組件1
const Settings = () => import('@/views/Settings.vue') // 子組件2const routes = [{path: '/admin',name: 'Layout',component: Layout,// 子路由配置:path不加/,自動拼接父路徑(/admin/dashboard)children: [{path: '', // 子路由默認路徑(訪問/admin時渲染Dashboard)component: Dashboard},{path: 'settings', // 子路由路徑:完整路徑為/admin/settingsname: 'Settings',component: Settings}]}
]
2. 父組件(Layout.vue
)添加子路由視圖
<!-- src/views/Layout.vue -->
<template><div class="layout"><!-- 側邊欄(固定父組件內容) --><aside><router-link :to="{ name: 'Dashboard' }">數據看板</router-link><router-link :to="{ name: 'Settings' }">系統設置</router-link></aside><!-- 子路由視圖出口:子組件(Dashboard/Settings)渲染到這里 --><main><router-view></router-view></main></div>
</template>
關鍵注意點
- 子路由
path
不加/
:自動拼接父路由路徑(如settings
→/admin/settings
);加/
則為絕對路徑(需寫完整路徑,如/admin/settings
)。 - 每級路由需對應
router-view
:父路由組件放父級router-view
,子路由組件放子級router-view
(路由有幾級,就需要幾個router-view
)。
3.2 動態路由(路徑參數)
動態路由用于“多個路徑對應同一個組件”的場景(如用戶詳情頁/user/1
、/user/2
均渲染User
組件),核心是通過:
聲明動態參數。
1. 配置示例(用戶詳情頁)
// src/router/index.js
const User = () => import('@/views/User.vue')const routes = [{// :id為動態參數,可自定義名稱(如:userId)path: '/user/:id', name: 'User',component: User,// 可選:通過props將params參數注入組件(避免在組件中寫$route.params)props: true }
]
2. 組件中獲取動態參數
<!-- src/views/User.vue -->
<template><div><!-- 方式1:直接通過$route.params獲取 --><h1>用戶ID:{{ $route.params.id }}</h1><!-- 方式2:通過props接收(需路由配置props: true) --><h1>用戶ID(props):{{ id }}</h1></div>
</template>
<script>
export default {name: 'User',props: ['id'], // 接收路由注入的params參數// 監聽參數變化(組件復用場景,如從/user/1跳轉到/user/2)watch: {$route(to, from) {console.log('用戶ID變化:', to.params.id)}}
}
</script>
3. 動態路由跳轉方式
跳轉方式 | 代碼示例 | 說明 |
---|---|---|
路徑字符串 | this.$router.push('/user/123') | 簡單直接,適合固定路徑 |
path對象 | this.$router.push({ path: '/user/123' }) | 路徑需完整拼接,不支持單獨傳params |
name+params | this.$router.push({ name: 'User', params: { id: 123 } }) | 推薦:解耦路徑,params自動拼接路徑 |
3.3 查詢參數(query)
查詢參數用于“非路徑必需的臨時數據傳遞”(如列表篩選、分頁),格式為URL?key=value&key2=value2
,無需預配置路由。
1. 跳轉與參數獲取示例
<!-- 跳轉組件(如Home.vue) -->
<template><button @click="goProductList">查看商品列表</button>
</template>
<script>
export default {methods: {goProductList() {// 方式1:path對象+querythis.$router.push({path: '/product',query: { category: 'phone', page: 1 } // 查詢參數})// 方式2:name對象+query(推薦,解耦路徑)// this.$router.push({// name: 'Product',// query: { category: 'phone', page: 1 }// })}}
}
</script>
<!-- 目標組件(Product.vue)獲取參數 -->
<template><div><h1>篩選分類:{{ $route.query.category }}</h1><h1>當前頁碼:{{ $route.query.page }}</h1></div>
</template>
2. 動態路由 vs 查詢參數(核心區別)
對比維度 | 動態路由(params) | 查詢參數(query) |
---|---|---|
路徑表現 | 是路徑的一部分(/user/123 ) | 附加在URL后(/product?category=phone ) |
路由配置 | 需預定義動態參數(/user/:id ) | 無需預配置 |
參數必要性 | 必選(不傳遞參數會導致路由匹配失敗) | 可選(不傳遞也能匹配路由) |
數據用途 | 標識資源(如用戶ID、商品ID) | 篩選、排序、分頁等臨時數據 |
參數獲取 | $route.params | $route.query |
3.4 通配符與404頁面
通配符*
用于匹配所有未明確定義的路由,常用來實現404頁面(提升用戶體驗,替代空白頁面)。
1. 配置示例
// src/router/index.js
const NotFound = () => import('@/views/NotFound.vue') // 404組件const routes = [// 其他路由配置...// 通配符路由:必須放在最后(確保優先匹配明確路由){path: '*',component: NotFound}
]
2. 404組件示例(NotFound.vue
)
<template><div class="not-found"><h1>404 - 頁面走丟了!</h1><p>您訪問的路徑:{{ $route.params.pathMatch }}</p> <!-- 獲取無效路徑 --><router-link to="/home">返回首頁</router-link></div>
</template>
<style scoped>
.not-found {text-align: center;margin-top: 50px;color: #666;
}
</style>
關鍵注意點
- 通配符位置:必須放在路由配置最后(舊版Vue Router 3.x嚴格要求,新版雖優化,但保持習慣可兼容)。
pathMatch
參數:通配符匹配時,$route.params.pathMatch
會自動包含用戶訪問的無效路徑,可用于頁面提示。
3.5 重定向與別名
1. 重定向(redirect)
重定向是“訪問A路徑時自動跳轉到B路徑”,地址欄會顯示B路徑的URL,適合舊路徑兼容、默認頁面跳轉。
重定向方式 | 代碼示例 | 適用場景 |
---|---|---|
字符串路徑 | redirect: '/home' | 簡單固定跳轉 |
name對象 | redirect: { name: 'Home' } | 依賴路由名稱,路徑變化無需修改 |
動態函數 | redirect: to => '/home' | 復雜邏輯(如根據參數動態跳轉) |
2. 別名(alias)
別名是“給路由多個訪問路徑”,訪問別名路徑時,組件不變且地址欄顯示別名,適合隱藏真實路由結構、兼容舊URL。
const routes = [{path: '/home',name: 'Home',component: Home,alias: '/index' // 訪問/index等同于訪問/home,地址欄顯示/index}
]
重定向 vs 別名(核心區別)
- 重定向:URL會變化(A→B),本質是“跳轉”;
- 別名:URL不變(訪問別名路徑),本質是“同一資源的多個入口”。
四、補充重點知識(性能與實戰技巧)
4.1 路由懶加載(性能優化)
路由懶加載通過“按需加載組件”減少首屏加載時間,核心是component: () => import('組件路徑')
語法(Webpack動態導入)。
1. 配置示例
// 懶加載寫法(推薦)
const Home = () => import('@/views/Home.vue')
// 對比:普通導入(首屏會加載所有組件,性能差)
// import Home from '@/views/Home.vue'const routes = [{ path: '/home', component: Home }
]
2. 優勢
- 首屏加載體積減小:僅加載當前路徑的組件,而非所有組件;
- 提升首屏渲染速度:避免因組件過多導致的白屏時間過長。
4.2 路徑別名@
的使用
@
是Webpack默認配置的路徑別名,代表src
目錄,可避免復雜的相對路徑(如../../views/Home.vue
)。
1. 使用示例
// 普通相對路徑(繁瑣,易出錯)
import Home from '../../views/Home.vue'
// @別名路徑(簡潔,項目結構變化無需修改)
import Home from '@/views/Home.vue'
2. 原理
Webpack配置中通過resolve.alias
定義:
// webpack.config.js(Vue CLI項目無需手動配置)
module.exports = {resolve: {alias: {'@': path.resolve(__dirname, 'src') // @指向src目錄}}
}
4.3 路由守衛基礎(權限控制)
路由守衛用于“路由跳轉前/后攔截”,常實現登錄驗證、權限判斷等功能,核心介紹全局前置守衛beforeEach
。
登錄驗證示例
// src/router/index.js
router.beforeEach((to, from, next) => {// 1. 不需要登錄的頁面(如首頁、登錄頁)直接放行const whiteList = ['/home', '/login']if (whiteList.includes(to.path)) {return next()}// 2. 需要登錄:判斷是否有tokenconst token = localStorage.getItem('token')if (token) {next() // 有token,放行} else {next('/login') // 無token,跳轉到登錄頁}
})
to
:即將進入的目標路由對象;from
:當前離開的路由對象;next
:必須調用的函數,控制是否放行(next()
放行,next('/login')
跳轉到指定路徑)。
五、知識小結
知識點 | 核心內容 | 考試重點/易混淆點 | 難度系數 |
---|---|---|---|
路由模式 | hash(帶#,純前端)、history(無#,需后端配置) | history模式404問題與Nginx配置 | ??? |
嵌套路由 | children 配置子路由,多級router-view 對應層級 | 子路由路徑加不加/ 的區別;router-view 數量與路由層級匹配 | ??? |
動態路由 | :param 聲明參數,$route.params 獲取,組件復用需監聽$route | 動態路由跳轉的三種方式;props: true 注入參數的用法 | ???? |
查詢參數 | URL?key=value ,$route.query 獲取,無需預配置路由 | 與動態路由的區別(必選vs可選、路徑部分vs附加部分) | ??? |
通配符與404 | * 匹配所有路徑,必須放路由最后,pathMatch 獲取無效路徑 | 通配符位置的影響;404頁面的用戶體驗設計 | ?? |
重定向與別名 | 重定向(URL變化)、別名(URL不變) | 兩者的本質區別;別名的實際應用場景 | ?? |
路由懶加載 | component: () => import() 按需加載,優化首屏性能 | 與普通導入的區別;懶加載對打包體積的影響 | ??? |
路由守衛 | beforeEach 全局攔截,實現登錄驗證、權限控制 | next() 函數的必選性;白名單的設計思路 | ???? |
六、實戰小練習
6.1 題目
- 動態路由實戰:實現“商品詳情頁”,要求:
- 配置動態路由
/product/:productId
,對應ProductDetail
組件; - 在
Home
組件中通過router-link
和編程式導航兩種方式跳轉到詳情頁(傳遞productId=1001
); - 在
ProductDetail
組件中顯示商品ID,并監聽ID變化(從1001
跳轉到1002
時打印新ID)。
- 配置動態路由
- 查詢參數實戰:實現“商品列表篩選”,要求:
- 配置路由
/product-list
,對應ProductList
組件; - 在
Home
組件中跳轉時傳遞查詢參數category=phone&sort=price
; - 在
ProductList
組件中顯示篩選分類和排序方式,并支持通過按鈕切換排序(sort=price
→sort=sales
)。
- 配置路由
- 404與重定向實戰:
- 創建
NotFound
組件,配置通配符路由實現404頁面; - 配置重定向:訪問
/
時跳轉到/home
,訪問/old-home
時跳轉到/home
(通過name對象)。
- 創建
- 嵌套路由實戰:實現“個人中心”嵌套結構,要求:
- 父路由
/profile
對應ProfileLayout
組件(包含“基本信息”“我的訂單”導航); - 子路由
/profile/basic
對應ProfileBasic
組件,/profile/orders
對應ProfileOrders
組件; - 訪問
/profile
時默認顯示ProfileBasic
組件。
- 父路由
6.2 參考答案
1. 動態路由實戰
(1)路由配置(src/router/index.js
)
const Home = () => import('@/views/Home.vue')
const ProductDetail = () => import('@/views/ProductDetail.vue')const routes = [{ path: '/home', name: 'Home', component: Home },{ path: '/product/:productId', name: 'ProductDetail', component: ProductDetail,props: true // 注入productId到props}
]
(2)跳轉組件(Home.vue
)
<template><div><h1>首頁</h1><!-- 方式1:router-link跳轉 --><router-link :to="{ name: 'ProductDetail', params: { productId: 1001 } }">查看商品1001(router-link)</router-link><!-- 方式2:編程式導航 --><button @click="goToProduct(1001)">查看商品1001(編程式)</button></div>
</template>
<script>
export default {methods: {goToProduct(id) {this.$router.push({ name: 'ProductDetail', params: { productId: id } })}}
}
</script>
(3)詳情組件(ProductDetail.vue
)
<template><div><h1>商品詳情頁</h1><p>商品ID:{{ productId }}</p><button @click="goToNextProduct">切換到商品1002</button></div>
</template>
<script>
export default {name: 'ProductDetail',props: ['productId'], // 接收路由注入的參數methods: {goToNextProduct() {this.$router.push({ name: 'ProductDetail', params: { productId: 1002 } })}},// 監聽參數變化watch: {productId(newId, oldId) {console.log(`商品ID從${oldId}變為${newId}`)}}
}
</script>
2. 查詢參數實戰
(1)路由配置(src/router/index.js
)
const ProductList = () => import('@/views/ProductList.vue')const routes = [{ path: '/product-list', name: 'ProductList', component: ProductList }
]
(2)跳轉組件(Home.vue
)
<template><button @click="goToProductList">查看手機商品列表</button>
</template>
<script>
export default {methods: {goToProductList() {this.$router.push({name: 'ProductList',query: { category: 'phone', sort: 'price' }})}}
}
</script>
(3)列表組件(ProductList.vue
)
<template><div><h1>商品列表</h1><p>篩選分類:{{ $route.query.category }}</p><p>排序方式:{{ $route.query.sort }}</p><button @click="changeSort">切換排序為“銷量”</button></div>
</template>
<script>
export default {methods: {changeSort() {// 保留當前category參數,僅修改sortthis.$router.push({name: 'ProductList',query: { ...this.$route.query, sort: 'sales' }})}}
}
</script>
3. 404與重定向實戰
(1)404組件(NotFound.vue
)
<template><div style="text-align: center; margin-top: 50px;"><h1 style="color: #f44336;">404 - 頁面不存在</h1><p>您訪問的路徑:{{ $route.params.pathMatch }}</p><router-link to="/home" style="color: #2196f3;">返回首頁</router-link></div>
</template>
(2)路由配置(src/router/index.js
)
const NotFound = () => import('@/views/NotFound.vue')const routes = [// 重定向:/ → /home,/old-home → /home{ path: '/', redirect: '/home' },{ path: '/old-home', redirect: { name: 'Home' } },{ path: '/home', name: 'Home', component: Home },// 通配符路由放最后{ path: '*', component: NotFound }
]
4. 嵌套路由實戰
(1)路由配置(src/router/index.js
)
const ProfileLayout = () => import('@/views/ProfileLayout.vue')
const ProfileBasic = () => import('@/views/ProfileBasic.vue')
const ProfileOrders = () => import('@/views/ProfileOrders.vue')const routes = [{path: '/profile',name: 'ProfileLayout',component: ProfileLayout,children: [{ path: '', component: ProfileBasic }, // 默認顯示基本信息{ path: 'basic', name: 'ProfileBasic', component: ProfileBasic },{ path: 'orders', name: 'ProfileOrders', component: ProfileOrders }]}
]
(2)父布局組件(ProfileLayout.vue
)
<template><div class="profile-layout"><!-- 側邊導航 --><aside><router-link :to="{ name: 'ProfileBasic' }">基本信息</router-link><router-link :to="{ name: 'ProfileOrders' }">我的訂單</router-link></aside><!-- 子路由視圖出口 --><main><router-view></router-view></main></div>
</template>
<style scoped>
.profile-layout {display: flex;
}
aside {width: 200px;border-right: 1px solid #eee;padding: 20px;
}
aside router-link {display: block;margin: 10px 0;text-decoration: none;
}
main {flex: 1;padding: 20px;
}
</style>
(3)子組件(ProfileBasic.vue
示例)
<template><div><h2>基本信息</h2><p>用戶名:張三</p><p>手機號:138****1234</p></div>
</template>