Vue3 路由系統(Vue Router 4) – router.currentRoute 完全攻略
簡介
在單頁應用(SPA)中,路由 負責把 URL 與畫面元件(Component)對應起來,讓使用者在不同頁面間切換而不需要重新載入整個網頁。Vue Router 4 是 Vue 3 官方推薦的路由解決方案,提供了直觀的 API 以及完整的 TypeScript 支援。
本篇聚焦在 router.currentRoute 這個屬性,它是取得目前「活躍路由」資訊的唯一入口。無論是顯示動態標題、判斷權限、或是根據 URL 參數載入資料,都離不開 currentRoute。了解它的結構、觀察方式與最佳實踐,能讓你的 Vue 應用更具彈性與可維護性。
核心概念
1. router.currentRoute 是什麼?
router.currentRoute 是一個 響應式的 RouteLocationNormalizedLoaded 物件,代表「當前已解析且已載入的路由」。它的屬性與 Vue Router 官方文件中的 RouteLocationNormalized 幾乎相同,常用的屬性包括:
| 屬性 | 型別 | 說明 |
|---|---|---|
path |
string |
完整的 URL 路徑(不含 query、hash) |
fullPath |
string |
含 query、hash 的完整路徑 |
name |
string | symbol | null |
路由名稱(若有設定) |
params |
Record<string, string> |
動態路由參數(如 /user/:id) |
query |
`Record<string, string | null>` |
hash |
string |
URL 中的 hash(#section) |
matched |
Array<RouteRecordNormalized> |
匹配的路由記錄陣列,從根到子路由依序排列 |
因為 router.currentRoute 是 reactive,在模板或 setup 中直接使用即可自動追蹤變化,無需額外的 watch。
Tip:在 Options API 中,可透過
this.$route取得同樣的資訊;在 Composition API 中則建議直接使用router.currentRoute,或使用useRoute()這個封裝好的 Hook。
2. 取得 router 實例
在 Vue 3 + Vue Router 4 中,我們通常在 main.js(或 main.ts)將路由掛載到應用:
import { createApp } from 'vue';
import { createRouter, createWebHistory } from 'vue-router';
import App from './App.vue';
import routes from './routes';
const router = createRouter({
history: createWebHistory(),
routes,
});
const app = createApp(App);
app.use(router);
app.mount('#app');
在任何組件中,只要透過 import { useRouter } from 'vue-router' 即可取得同一個 router 實例:
import { useRouter } from 'vue-router';
export default {
setup() {
const router = useRouter(); // <-- 取得 router
// ...
return { router };
},
};
3. 直接讀取當前路由資訊
以下示範在 setup 中直接解構 currentRoute,並在模板中顯示:
// MyHeader.vue
<script setup>
import { useRouter } from 'vue-router';
import { computed } from 'vue';
const router = useRouter();
const currentPath = computed(() => router.currentRoute.value.path);
const currentName = computed(() => router.currentRoute.value.name);
</script>
<template>
<header>
<h1>目前路徑:{{ currentPath }}</h1>
<p>路由名稱:{{ currentName }}</p>
</header>
</template>
重點:
router.currentRoute本身是ref,因此在 Composition API 中需要加上.value取得實際物件。
4. 監聽路由變化
雖然 router.currentRoute 已是響應式,但有時我們想在路由變動時執行副作用(例如發送 API 請求)。可以使用 watch:
import { watch } from 'vue';
import { useRouter } from 'vue-router';
import { fetchUserData } from '@/api';
export default {
setup() {
const router = useRouter();
// 監聽整個 route 物件
watch(
() => router.currentRoute.value.fullPath,
(newFullPath, oldFullPath) => {
console.log('路由變更:', oldFullPath, '=>', newFullPath);
// 假設路由包含 userId,則根據變更重新抓資料
const userId = router.currentRoute.value.params.id;
if (userId) {
fetchUserData(userId).then(/* ... */);
}
}
);
return {};
},
};
如果只想監聽某個特定欄位(例如 params.id),可以更精細:
watch(
() => router.currentRoute.value.params.id,
(newId, oldId) => {
if (newId !== oldId) {
// 只在 id 變動時觸發
}
}
);
5. 使用 useRoute() 的簡化寫法
Vue Router 4 同時提供 useRoute(),它會返回 同樣的 currentRoute 物件,但不需要先取得 router:
import { useRoute } from 'vue-router';
import { computed } from 'vue';
export default {
setup() {
const route = useRoute();
const pageTitle = computed(() => {
// 假設每個路由都有 meta.title
return route.meta.title || '未命名頁面';
});
return { pageTitle };
},
};
useRoute() 在組件內部是 唯一的(即每個呼叫都返回同一個響應式物件),因此可以放心在多個 composable 中使用。
程式碼範例
以下提供 5 個實用範例,涵蓋從基本讀取到進階的路由資訊運用。
範例 1️⃣:根據路由動態設定 <title>
// usePageTitle.js
import { watchEffect } from 'vue';
import { useRoute } from 'vue-router';
export function usePageTitle() {
const route = useRoute();
// 每次路由變動都自動更新 document.title
watchEffect(() => {
const title = route.meta.title || 'My Vue App';
document.title = `${title} - My Vue App`;
});
}
在任何頁面組件的 setup 中只要呼叫 usePageTitle() 即可:
import { usePageTitle } from '@/composables/usePageTitle';
export default {
setup() {
usePageTitle();
return {};
},
};
範例 2️⃣:使用 router.currentRoute 判斷是否顯示側邊欄
// Layout.vue
<script setup>
import { useRouter } from 'vue-router';
import { computed } from 'vue';
const router = useRouter();
// 假設在登入頁與錯誤頁不顯示側邊欄
const showSidebar = computed(() => {
const hideNames = ['Login', 'NotFound'];
return !hideNames.includes(router.currentRoute.value.name);
});
</script>
<template>
<div class="layout">
<Sidebar v-if="showSidebar" />
<router-view />
</div>
</template>
範例 3️⃣:根據 query 參數切換列表排序
// ProductList.vue
<script setup>
import { useRoute, useRouter } from 'vue-router';
import { ref, watchEffect } from 'vue';
import { fetchProducts } from '@/api';
const route = useRoute();
const router = useRouter();
const sort = ref(route.query.sort || 'price'); // 預設依價格排序
const products = ref([]);
// 監聽 query 變化,重新抓資料
watchEffect(() => {
const currentSort = route.query.sort || 'price';
if (currentSort !== sort.value) {
sort.value = currentSort;
loadData();
}
});
function loadData() {
fetchProducts({ sort: sort.value }).then(res => {
products.value = res.data;
});
}
// 用於 UI 按鈕,切換排序
function changeSort(newSort) {
router.replace({ query: { ...route.query, sort: newSort } });
}
</script>
<template>
<div>
<button @click="changeSort('price')">價格排序</button>
<button @click="changeSort('rating')">評分排序</button>
<ul>
<li v-for="p in products" :key="p.id">{{ p.name }} - {{ p.price }}</li>
</ul>
</div>
</template>
範例 4️⃣:取得巢狀路由的父層資訊
// AdminDashboard.vue(子路由)
<script setup>
import { useRoute } from 'vue-router';
import { computed } from 'vue';
const route = useRoute();
// matched 陣列的第一項是根路由,最後一項是當前子路由
const parentRoute = computed(() => route.matched[0].name); // 例如 'Admin'
const currentRouteName = computed(() => route.name);
</script>
<template>
<h2>目前子頁面:{{ currentRouteName }}</h2>
<p>父層路由名稱:{{ parentRoute }}</p>
</template>
範例 5️⃣:在全局導航守衛中使用 to 與 router.currentRoute 比較
// router/index.js
router.beforeEach((to, from, next) => {
// 假設需要在離開編輯頁時確認是否保存
if (from.name === 'EditArticle' && !store.state.articleSaved) {
const answer = window.confirm('尚未儲存,確定離開嗎?');
if (!answer) return; // 取消離開
}
next();
});
在上述守衛中,我們直接使用 from(即 router.currentRoute 的前一個值)來判斷是否需要提示使用者。
常見陷阱與最佳實踐
| 陷阱 | 說明 | 解決方案 |
|---|---|---|
忘記 .value |
在 Composition API 中 router.currentRoute 是 ref,直接取屬性會得到 undefined。 |
必須使用 router.currentRoute.value.xxx,或使用 useRoute() 直接取得響應式物件。 |
在 setup 外直接讀取 |
在 Options API 的 created、mounted 等生命週期中直接讀 router.currentRoute 可能尚未完成解析。 |
使用 onBeforeRouteUpdate 或 watch 觀察 fullPath,確保取得的資訊已正確。 |
過度依賴 params |
動態路由參數如果未在路由定義中提供,params 會是空字串,導致 API 請求失敗。 |
在取得前先檢查 router.currentRoute.value.params 是否存在,或使用 route.params?.id ?? ''。 |
| 在全局守衛中直接改變路由 | 在 beforeEach、beforeResolve 等守衛裡使用 router.push 可能造成無限迴圈。 |
改用 next({ name: 'Login' }) 或 return { name: 'Login' } 的方式跳轉。 |
忽略 hash |
有時候同一路由但不同 hash(如 #section1、#section2)仍會觸發 watch,造成不必要的重新渲染。 |
若只關心路徑,可監聽 router.currentRoute.value.path,或在 watch 裡過濾 hash。 |
最佳實踐:
- 盡量使用
useRoute():它讓程式碼更簡潔,且不需要先取得router。 - 使用
computed包裝路由資訊:避免在模板裡直接寫長條表達式,提升可讀性。 - 在需要副作用時才使用
watch:大多數 UI 只需要讀取路由,不必額外監聽。 - 保持路由定義的
meta統一:例如title、requiresAuth,在組件裡只要讀route.meta,不必硬編碼。 - 利用
router.replace替代push:當僅修改 query 或 hash 而不想留下歷史紀錄時使用replace,提升使用者體驗。
實際應用場景
動態文件標題與 Breadcrumb
- 透過
router.currentRoute.meta.title產生<title>,同時根據matched陣列組合層級導航(Breadcrumb)。
- 透過
權限控制
- 在全局守衛或 layout 組件中檢查
router.currentRoute.meta.requiresAuth,若未登入則導向登入頁。
- 在全局守衛或 layout 組件中檢查
多語系切換
- 依據
router.currentRoute.path或name取得對應的翻譯 key,動態切換 UI 文本。
- 依據
表單編輯與離開提示
- 監聽
router.currentRoute.value.fullPath,在使用者離開當前編輯頁面(包括點擊瀏覽器返回)時彈出確認對話框。
- 監聽
API 請求快取
- 以
router.currentRoute.value.fullPath為快取鍵,避免在同一路由下重複發送相同的資料請求。
- 以
總結
router.currentRoute 是 Vue Router 4 中取得 當前路由資訊 的核心入口,具備完整的路由屬性、響應式特性以及與 useRoute() 的等價性。透過正確的 解構、觀察與使用,開發者可以:
- 即時更新 UI(如頁面標題、側邊欄顯示)
- 依據路由參數觸發資料載入
- 在全局守衛中實作權限或離開提醒
同時,避免常見的 .value 忘記、過度監聽或守衛中循環跳轉等陷阱,遵循「只在需要時才觀察」的原則,能讓應用保持高效且易於維護。希望透過本篇的概念說明與實務範例,你能在自己的 Vue3 專案中更自信地運用 router.currentRoute,打造流暢、可擴充的單頁應用。祝開發順利! 🚀