前端路由的作用
- 傳統網站:每點一個連結,瀏覽器就向伺服器要一個新的 HTML 頁面,整頁重新載入
- SPA(Single Page Application):從頭到尾只有一個 HTML 頁面,內容靠 JavaScript 動態替換
- 但 SPA 還是得處理 URL 對應、瀏覽器上一頁/下一頁、直接輸入網址這些行為——這就是前端路由的工作
- Vue Router 是 Vue 官方的路由方案,負責把 URL 對應到元件
基本設定
安裝套件(create-vue 建專案時若已選 Router 則不需要):
npm install vue-router
建立路由
// router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import Home from '@/views/Home.vue'
import About from '@/views/About.vue'
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/about',
name: 'About',
component: About
}
]
const router = createRouter({
history: createWebHistory(),
routes
})
export default router
掛載到 app
// main.js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
createApp(App).use(router).mount('#app')
在 template 裡使用
<!-- App.vue -->
<template>
<nav>
<RouterLink to="/">首頁</RouterLink>
<RouterLink to="/about">關於</RouterLink>
</nav>
<!-- 路由匹配到的元件會渲染在這裡 -->
<RouterView />
</template>
<RouterLink>— Vue Router 版的<a>標籤,點了不會整頁刷新,而是走前端路由切換<RouterView>— 佔位元件,當前路由匹配到的元件就渲染在這裡
createWebHistory vs createWebHashHistory
// URL 長這樣:example.com/about
createWebHistory()
// URL 長這樣:example.com/#/about
createWebHashHistory()
| 模式 | URL 格式 | 後端配合 | 備註 |
|---|---|---|---|
createWebHistory | example.com/about | 需要(所有路徑導向 index.html) | 主流部署平台(Vercel、Netlify)自動處理,推薦使用 |
createWebHashHistory | example.com/#/about | 不需要(# 後的內容不送到伺服器) | URL 較不美觀 |
如果是自己架伺服器,後端要設 rewrite 把所有路徑指回 index.html,不然使用者一按重新整理就 404:
// vercel.json
{
"rewrites": [{ "source": "/(.*)", "destination": "/index.html" }]
}
# nginx.conf
location / {
try_files $uri $uri/ /index.html;
}
動態路由
URL 裡有會變動的片段(像 /user/123、/post/456),就用動態路由:
const routes = [
{
path: '/user/:id',
name: 'User',
component: UserProfile
}
]
:id 就是動態參數,在元件裡用 useRoute() 拿:
<script setup>
import { useRoute } from 'vue-router'
const route = useRoute()
console.log(route.params.id) // '123'
</script>
多個動態參數
// /user/jeremy/posts/42
{
path: '/user/:username/posts/:postId',
component: UserPost
}
監聽參數變化
有個坑:從 /user/123 導航到 /user/456,Vue 會複用同一個元件實例,所以 onMounted 不會重跑。想跟著參數變化做事,要用 watch:
<script setup>
import { watch } from 'vue'
import { useRoute } from 'vue-router'
const route = useRoute()
watch(
() => route.params.id,
(newId) => {
// 重新取得資料
fetchUser(newId)
}
)
</script>
巢狀路由
常見需求:外層共用一個 layout(像 sidebar),中間的內容區跟著子路由切換。
/settings
├── /settings/profile → 個人資料
├── /settings/security → 安全設定
└── /settings/notify → 通知設定
用巢狀路由就能做到:
const routes = [
{
path: '/settings',
component: SettingsLayout,
children: [
{
path: '', // /settings 的預設頁面
component: SettingsProfile
},
{
path: 'profile', // /settings/profile
component: SettingsProfile
},
{
path: 'security', // /settings/security
component: SettingsSecurity
},
{
path: 'notify', // /settings/notify
component: SettingsNotify
}
]
}
]
然後在 SettingsLayout.vue 裡放一個 <RouterView>,當子路由的出口:
<!-- SettingsLayout.vue -->
<template>
<div class="settings-page">
<aside>
<RouterLink to="/settings/profile">個人資料</RouterLink>
<RouterLink to="/settings/security">安全設定</RouterLink>
<RouterLink to="/settings/notify">通知設定</RouterLink>
</aside>
<main>
<!-- 子路由的元件會渲染在這裡 -->
<RouterView />
</main>
</div>
</template>
核心概念:外層的 <RouterView> 渲染父路由的元件,內層的 <RouterView> 渲染子路由的元件,要疊幾層都可以。
導航守衛
導航守衛會在路由切換前攔下來,檢查過了才放行。最常見的用途就是權限控制。
全域守衛
// router/index.js
router.beforeEach((to, from) => {
const isLoggedIn = !!localStorage.getItem('token')
// 要去的頁面需要登入,但使用者沒登入
if (to.meta.requiresAuth && !isLoggedIn) {
// 導向登入頁
return { name: 'Login' }
}
})
搭配路由的 meta 欄位:
const routes = [
{
path: '/dashboard',
component: Dashboard,
meta: { requiresAuth: true }
},
{
path: '/login',
name: 'Login',
component: Login
}
]
路由獨享守衛
只想管某一條路由的話,寫在路由本身:
{
path: '/admin',
component: Admin,
beforeEnter: (to, from) => {
const user = useAuthStore()
if (user.role !== 'admin') {
return { name: 'Home' }
}
}
}
元件內守衛
onBeforeRouteLeave 可以在元件裡攔截「要離開這頁」的時機,最典型的用法是提醒使用者還有沒存檔的修改:
<script setup>
import { onBeforeRouteLeave } from 'vue-router'
const hasUnsavedChanges = ref(false)
onBeforeRouteLeave((to, from) => {
if (hasUnsavedChanges.value) {
const answer = confirm('你有未儲存的修改,確定要離開嗎?')
if (!answer) return false // 取消導航
}
})
</script>
守衛的執行順序
從 A 頁面導航到 B 頁面,守衛依序執行:
- A 的
onBeforeRouteLeave - 全域
beforeEach - B 的
beforeEnter(如果有) - B 的元件被解析
- 全域
afterEach
懶載入
如果你的 app 有 50 個頁面,使用者打開首頁的時候就要下載全部 50 個頁面的 JS?太浪費了。
懶載入(Lazy Loading)讓每個頁面的程式碼在需要的時候才載入。做法超簡單,把 import 改成動態的就好:
const routes = [
{
path: '/',
component: Home // 首頁直接載入
},
{
path: '/about',
// 動態 import → 只有使用者點進 /about 才會載入這個元件的 JS
component: () => import('@/views/About.vue')
},
{
path: '/dashboard',
component: () => import('@/views/Dashboard.vue')
}
]
Vite 和 Webpack 都會自動把動態 import 的部分拆成獨立的 JS 檔案(code splitting)。使用者進首頁只會下載首頁的 JS,點到 About 才會額外下載 About 的 JS。
分組打包
如果有些頁面通常會一起被訪問,你可以把它們打包在一起:
// 這三個會被打包在同一個 chunk 裡
component: () => import(/* webpackChunkName: "settings" */ '@/views/SettingsProfile.vue')
component: () => import(/* webpackChunkName: "settings" */ '@/views/SettingsSecurity.vue')
component: () => import(/* webpackChunkName: "settings" */ '@/views/SettingsNotify.vue')
不過如果你用 Vite 的話,它的 tree-shaking 已經做得很好了,大部分情況不需要手動分組。
實用技巧
程式化導航
除了用 <RouterLink>,你也可以在 JS 裡面控制導航:
import { useRouter } from 'vue-router'
const router = useRouter()
// 跳到某個路由
router.push('/about')
router.push({ name: 'User', params: { id: '123' } })
// 替換(不會留下歷史紀錄,按上一頁不會回來)
router.replace('/login')
// 前進/後退
router.go(-1) // 等同於按「上一頁」
router.go(1) // 等同於按「下一頁」
Query 參數
// URL: /search?q=vue&page=1
router.push({ path: '/search', query: { q: 'vue', page: 1 } })
// 在元件裡取得
const route = useRoute()
console.log(route.query.q) // 'vue'
console.log(route.query.page) // '1'(注意是字串)
404 頁面
{
path: '/:pathMatch(.*)*',
name: 'NotFound',
component: () => import('@/views/NotFound.vue')
}
把這個放在路由設定的最後面,所有沒匹配到的路徑都會導到 404 頁面。
滾動行為
切換路由時預設不會回到頂部。加上 scrollBehavior 可以控制:
const router = createRouter({
history: createWebHistory(),
routes,
scrollBehavior(to, from, savedPosition) {
// 如果有儲存位置(瀏覽器上一頁/下一頁),回到那個位置
if (savedPosition) return savedPosition
// 否則回到頂部
return { top: 0 }
}
})
小結
| 概念 | 一句話 |
|---|---|
| 基本路由 | URL 對應元件,用 RouterView 渲染 |
| 動態路由 | :id 參數,用 route.params 取得 |
| 巢狀路由 | children + 多層 RouterView |
| 導航守衛 | 路由保全,檢查權限才放行 |
| 懶載入 | () => import() 用到才載入 |
Vue Router 的東西其實還有很多(transition、keep-alive、命名視圖等),但上面這些已經涵蓋了日常開發 90% 的需求。把這些搞懂,你就能建構一個有完整路由系統的 Vue 應用了。
Latest Updates
- 2026.06.11 Content updated
