本文 AI 產出,尚未審核

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.currentRoutereactive,在模板或 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️⃣:在全局導航守衛中使用 torouter.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.currentRouteref,直接取屬性會得到 undefined 必須使用 router.currentRoute.value.xxx,或使用 useRoute() 直接取得響應式物件。
setup 外直接讀取 在 Options API 的 createdmounted 等生命週期中直接讀 router.currentRoute 可能尚未完成解析。 使用 onBeforeRouteUpdatewatch 觀察 fullPath,確保取得的資訊已正確。
過度依賴 params 動態路由參數如果未在路由定義中提供,params 會是空字串,導致 API 請求失敗。 在取得前先檢查 router.currentRoute.value.params 是否存在,或使用 route.params?.id ?? ''
在全局守衛中直接改變路由 beforeEachbeforeResolve 等守衛裡使用 router.push 可能造成無限迴圈。 改用 next({ name: 'Login' })return { name: 'Login' } 的方式跳轉。
忽略 hash 有時候同一路由但不同 hash(如 #section1#section2)仍會觸發 watch,造成不必要的重新渲染。 若只關心路徑,可監聽 router.currentRoute.value.path,或在 watch 裡過濾 hash

最佳實踐

  1. 盡量使用 useRoute():它讓程式碼更簡潔,且不需要先取得 router
  2. 使用 computed 包裝路由資訊:避免在模板裡直接寫長條表達式,提升可讀性。
  3. 在需要副作用時才使用 watch:大多數 UI 只需要讀取路由,不必額外監聽。
  4. 保持路由定義的 meta 統一:例如 titlerequiresAuth,在組件裡只要讀 route.meta,不必硬編碼。
  5. 利用 router.replace 替代 push:當僅修改 query 或 hash 而不想留下歷史紀錄時使用 replace,提升使用者體驗。

實際應用場景

  1. 動態文件標題與 Breadcrumb

    • 透過 router.currentRoute.meta.title 產生 <title>,同時根據 matched 陣列組合層級導航(Breadcrumb)。
  2. 權限控制

    • 在全局守衛或 layout 組件中檢查 router.currentRoute.meta.requiresAuth,若未登入則導向登入頁。
  3. 多語系切換

    • 依據 router.currentRoute.pathname 取得對應的翻譯 key,動態切換 UI 文本。
  4. 表單編輯與離開提示

    • 監聽 router.currentRoute.value.fullPath,在使用者離開當前編輯頁面(包括點擊瀏覽器返回)時彈出確認對話框。
  5. API 請求快取

    • router.currentRoute.value.fullPath 為快取鍵,避免在同一路由下重複發送相同的資料請求。

總結

router.currentRoute 是 Vue Router 4 中取得 當前路由資訊 的核心入口,具備完整的路由屬性、響應式特性以及與 useRoute() 的等價性。透過正確的 解構、觀察與使用,開發者可以:

  • 即時更新 UI(如頁面標題、側邊欄顯示)
  • 依據路由參數觸發資料載入
  • 在全局守衛中實作權限或離開提醒

同時,避免常見的 .value 忘記、過度監聽或守衛中循環跳轉等陷阱,遵循「只在需要時才觀察」的原則,能讓應用保持高效且易於維護。希望透過本篇的概念說明與實務範例,你能在自己的 Vue3 專案中更自信地運用 router.currentRoute,打造流暢、可擴充的單頁應用。祝開發順利! 🚀