Vue3 路由系統(Vue Router 4)
主題:定義 routes 陣列
簡介
在單頁應用(SPA)中,路由是連結不同畫面、管理 URL 與組件之間關係的核心機制。Vue 3 搭配 Vue Router 4,提供了直覺且彈性的 API,讓開發者只需要在一個 routes 陣列裡描述「路徑 ↔ 組件」的對應,即可完成整個導覽流程。
正確地設計 routes 陣列,不僅能提升程式可讀性、維護性,還能避免常見的 404 錯誤、路由守衛失效 或 懶載入失敗 等問題。本文將從概念說明、實作範例、常見陷阱與最佳實踐,帶你一步步掌握 Vue Router 4 中 routes 陣列的寫法與應用。
核心概念
1. routes 陣列的基本結構
routes 是一個 JavaScript 陣列,每個元素都是一個路由紀錄(route record),最常見的屬性包括:
| 屬性 | 型別 | 說明 |
|---|---|---|
path |
string |
URL 匹配字串,必填。 |
component |
Component |
與 path 對應的 Vue 組件,必填。 |
name |
string |
路由的命名,可在程式碼中以名稱跳轉。 |
meta |
object |
自訂資料,常用於權限、標題等。 |
children |
Array |
子路由,形成巢狀結構。 |
redirect |
`string | object` |
alias |
`string | Array` |
props |
`boolean | object |
重點:
path必須以「/」開頭,且同一層級不可有重複路徑,否則匹配結果會不確定。
2. 最簡單的範例
// src/router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '@/views/HomeView.vue'
import AboutView from '@/views/AboutView.vue'
const routes = [
{ path: '/', component: HomeView, name: 'home' },
{ path: '/about', component: AboutView, name: 'about' }
]
const router = createRouter({
history: createWebHistory(),
routes
})
export default router
HomeView 與 AboutView 直接對應到根路徑與 /about,透過 router-link 即可在畫面間切換。
3. 命名路由與程式碼跳轉
// 在組件內部使用
this.$router.push({ name: 'about' })
使用 命名路由 可以避免硬編碼 URL,當路徑變更時只需要在 routes 陣列裡調整 path,程式碼不需要變動。
4. 巢狀路由(Nested Routes)
import UserLayout from '@/layouts/UserLayout.vue'
import UserProfile from '@/views/user/Profile.vue'
import UserSettings from '@/views/user/Settings.vue'
const routes = [
{
path: '/user',
component: UserLayout,
children: [
{ path: '', component: UserProfile, name: 'user-profile' }, // /user
{ path: 'settings', component: UserSettings, name: 'user-settings' } // /user/settings
]
}
]
UserLayout會渲染<router-view>,其內部再根據子路由顯示不同的子畫面。- 子路由的
path不需要 以「/」開頭,會自動拼接父層路徑。
5. 懶載入(Lazy‑Loading)與代碼分割
在大型專案中,按需載入 可以顯著減少首屏載入時間。Vue Router 4 支援原生的動態 import()。
const routes = [
{
path: '/dashboard',
name: 'dashboard',
component: () => import('@/views/Dashboard.vue') // <-- 懶載入
},
{
path: '/reports',
name: 'reports',
component: () => import('@/views/Reports.vue')
}
]
Webpack(或 Vite)會自動將每個懶載入的組件拆分成獨立的 chunk,只有在路由被訪問時才會下載。
6. meta 欄位的實務運用
meta 常被用來存放 權限資訊、頁面標題、breadcrumb 等自訂屬性,配合全域守衛(navigation guard)即可實現權限驗證或動態改變 <title>。
const routes = [
{
path: '/admin',
component: () => import('@/views/Admin.vue'),
meta: { requiresAuth: true, title: '管理後台' }
},
{
path: '/login',
component: () => import('@/views/Login.vue'),
meta: { guestOnly: true }
}
]
// 全域前置守衛範例
router.beforeEach((to, from, next) => {
const isLoggedIn = !!localStorage.getItem('token')
if (to.meta.requiresAuth && !isLoggedIn) {
return next({ name: 'login' })
}
if (to.meta.guestOnly && isLoggedIn) {
return next({ name: 'home' })
}
document.title = to.meta.title || 'Vue App'
next()
})
7. 參數路由與 props 傳遞
// routes
{
path: '/product/:id',
name: 'product-detail',
component: () => import('@/views/ProductDetail.vue'),
props: true // 直接把 route.params 轉成 props
}
<!-- ProductDetail.vue -->
<template>
<div>商品編號:{{ id }}</div>
</template>
<script setup>
defineProps(['id'])
</script>
使用 props: true 可以讓組件 更易於單元測試,因為它不必依賴 $route。
8. 重新導向(Redirect)與別名(Alias)
// 重新導向:舊網址導向新網址
{ path: '/old-home', redirect: '/' },
// 別名:同一組件有多個入口 URL
{ path: '/help', alias: '/support', component: HelpView }
redirect 會改變瀏覽器的 URL,而 alias 則保持原 URL,只是渲染同一個組件,適合 SEO 或舊系統兼容。
常見陷阱與最佳實踐
| 陷阱 | 說明 | 解決方式 |
|---|---|---|
| 路徑重複 | 同層級出現相同 path 會導致匹配衝突。 |
用 name 或 alias 區分,或調整層級結構。 |
忘記在父層加入 <router-view> |
子路由不會被渲染。 | 確認父組件內有 <router-view />。 |
| 懶載入路徑錯誤 | import() 路徑寫錯會導致 404。 |
使用絕對別名 @/ 或相對路徑,並在 IDE 中確認。 |
props 未設定 |
需要路由參數時仍透過 $route.params,降低可測試性。 |
盡量使用 props: true 或自訂函式返回 props。 |
全域守衛忘記 next() |
導航卡住,畫面不會變更。 | 確保每條分支都有 next() 或 return next(...)。 |
| Meta 資料過度耦合 | 把太多業務邏輯塞入 meta。 |
只放與路由相關的資訊(如權限、標題),其他邏輯放在 store 或 composable。 |
最佳實踐
- 統一管理:將所有路由定義放在
src/router/index.js,或分模組(module)管理,最後在createRouter時合併。 - 使用命名路由:避免硬編碼 URL,提升重構彈性。
- 懶載入 + Code Splitting:對於非首屏頁面一定使用懶載入。
- 保持路由平坦:除非真的需要巢狀 UI,否則盡量避免過深的子路由,減少維護成本。
- 在
meta中加入title,配合全域守衛自動更新<title>,提升使用者體驗與 SEO。
實際應用場景
1. 電商平台的商品頁面
- 需求:商品列表、商品細節、搜尋結果皆需 SEO 友善的 URL。
- 實作:使用動態參數
/:category/:id,並在meta.title中加入商品名稱。
{
path: '/:category/:id',
name: 'product',
component: () => import('@/views/Product.vue'),
props: route => ({ id: route.params.id, category: route.params.category }),
meta: { title: route => `商品 - ${route.params.id}` }
}
2. 企業內部系統的權限控管
- 需求:不同角色只能進入特定功能。
- 實作:在
meta加入roles: ['admin', 'editor'],全域守衛根據使用者角色決定是否允許。
{
path: '/admin/users',
component: () => import('@/views/AdminUsers.vue'),
meta: { requiresAuth: true, roles: ['admin'] }
}
3. 多語系網站的別名路由
- 需求:同一頁面有中文與英文 URL。
- 實作:利用
alias為每個語系提供別名。
{
path: '/about',
alias: ['/about-zh', '/about-en'],
component: AboutView,
meta: { title: 'About Us' }
}
4. 大型 Dashboard 的懶載入
- 需求:首屏僅載入概覽,其餘圖表與報表在點擊時才下載。
- 實作:將每個子頁面設為懶載入,並配合
webpackChunkName自訂 chunk 名稱。
{
path: '/dashboard/analytics',
name: 'analytics',
component: () => import(/* webpackChunkName: "dashboard-analytics" */ '@/views/Analytics.vue')
}
總結
routes陣列是 Vue Router 4 的靈魂,透過path、component、name、meta、children等屬性,我們可以清晰地描述應用程式的導覽結構。- 懶載入、命名路由、meta 資料是提升效能與可維護性的關鍵技巧。
- 常見的陷阱(路徑衝突、忘記
<router-view>、懶載入路徑錯誤)只要遵守 最佳實踐——統一管理、使用命名、保持路由平坦——就能有效避免。 - 在實務開發中,根據 權限控管、SEO、代碼分割 等需求靈活運用
redirect、alias、props以及全域守衛,能讓路由系統不僅功能完整,也更具彈性與可測試性。
掌握了 routes 陣列的寫法與最佳實踐,你就能在 Vue3 專案中建立一個 結構清晰、效能優秀、易於維護 的路由系統,為之後的功能擴充與團隊協作奠定穩固基礎。祝開發順利! 🚀