Skip to content

Vue Router 動態路由教學:打造靈活的頁面切換機制

Vue Router 動態路由:/user/:id 與 useRoute

當開發大型應用程式(例如:電子商務、部落格)時,頁面數量往往多不勝數。我們不可能為每個商品 ID 手動撰寫 靜態路由,這時 動態路徑參數 (Dynamic Route Matching) 就是解決問題的核心技術。

什麼是動態路由?

動態路由允許我們將符合特定模式的網址,映射到同一個 Vue 元件上。例如:不管是 /product/1 還是 /product/999,都會共用同一個 Product.vue 元件,並根據 ID 顯示不同的內容。

專案結構建議

我們將新增一個 Product.vue,用來模擬根據不同 ID 顯示不同產品內容的場景。

src/
 ├─ router/
 │   └─ index.js      # 路由配置檔
 ├─ views/
 │   ├─ Home.vue
 │   ├─ About.vue
 │   └─ Product.vue   # 共用的動態顯示元件
 ├─ App.vue
 └─ main.js

路由表設定:使用冒號 : 佔位符

router/index.js 中,我們透過 冒號 來定義動態參數。這告訴 Vue Router,這段網址是變動的。

js
// /router/index.js
import { createRouter, createWebHistory } from 'vue-router';

export default createRouter({
  history: createWebHistory(),
  routes: [
    {
      path: '/',
      name: 'home',
      component: () => import('@/views/Home.vue'),
    },
    {
      path: '/about',
      name: 'about',
      component: () => import('@/views/About.vue'),
    },
    {
      path: '/product/:id', // :id 即為動態參數,可匹配 /product/a123
      name: 'product',
      component: () => import('@/views/Product.vue'),
    },
    {
      path: '/:pathMatch(.*)*',
      redirect: '/',
    },
  ],
});

參數也能加上限制與選填

  • 限制格式/product/:id(\\d+) 只接受數字,字母開頭的網址會落到 404 而不是進入頁面後才出錯。
  • 選填參數/product/:id? 表示 id 可有可無,/product 也會匹配到同一個元件。

在元件中取得動態參數

在元件中,我們可以使用 $route.params 來取得網址上的參數。亦可使用 Vue 3 Composition API 中 useRoute 函式來存取目前的路由資訊。

vue
<script setup>
import { useRoute } from "vue-router";

const route = useRoute();

// 在 JS 中取得參數的方法
console.log("產品 ID:", route.params.id);
</script>

<template>
  <h1>產品頁面</h1>
  <p>目前查看的產品 ID 是:{{ $route.params.id }}</p>
</template>

useRoute 還是 useRouter?

  • useRoute():讀 目前路由的資訊,例如:paramsquerymeta
  • useRouter():拿到 路由器本身,用來 pushreplaceback 做跳轉。

一句話記法:讀資料用 route,做動作用 router。另外要注意 route.params.id 取到的一律是 字串,需要數字時記得自行轉換。

TIP

同一元件重複使用 : 當您從 /product/1 切換到 /product/2 時,Vue 為了效能會重複使用同一個元件實體,因此 onMounted 不會再次觸發。如果需要監控參數變化,可以使用 watchwatchEffect 監聽 route.params

元件重用的實務寫法

上面的提醒是動態路由最常見的坑:切換 ID 時 onMounted 不會再跑,資料自然不會更新。正確做法是把 取資料 抽成函式,掛載時執行一次,之後交給 watch 監聽參數:

vue
<script setup>
import { ref, watch } from "vue";
import { useRoute } from "vue-router";
import axios from "axios";

const route = useRoute();
const product = ref(null);

async function fetchProduct(id) {
  const res = await axios.get(`/api/products/${id}`);
  product.value = res.data;
}

// immediate 讓它在初次進入時就先跑一次,等同於原本 onMounted 的工作
watch(() => route.params.id, fetchProduct, { immediate: true });
</script>

API 請求的完整錯誤處理寫法,可以參考 Axios 使用

建立導覽連結

在導覽中,我們可以透過字串模板或是物件形式來傳遞動態參數。

vue
<!-- /App.vue -->
<template>
  <router-link to="/">首頁</router-link> |
  <router-link to="/about">關於</router-link> |
  <router-link to="/product/a123">產品 A</router-link> |
  <router-link
    :to="{
      name: 'product',
      params: {
        id: 'b456',
      },
    }"
    >產品 B</router-link
  >

  <RouterView />
</template>

<style>
.router-link-active {
  font-weight: bold;
  color: #42b983;
}
</style>

物件形式雖然長了一點,但 只依賴路由名稱,日後路徑調整時連結不必跟著改,這也是建議加上 name 的原因。

深度比較:動態路由 (Params) vs 查詢參數 (Query)

開發者常糾結該使用哪一種方式傳遞參數,以下是關鍵差異對照表:

特性動態路由(Params)查詢參數 (Query)
網址範例/product/123/product?id=123
定義方式需在路由表定義 :id不需要預先定義
取得方式$route.params.id$route.query.id
語意代表 資源 的一部分 (必要)代表 過濾/排序 條件 (非必要)
SEO 影響較佳,有利於搜尋引擎理解層級普通,通常被視為同一頁的不同狀態

開發小技巧

  • 使用 Params:當您希望這個網址代表一個 獨立頁面 時,例如:/user/away
  • 使用 Query:當您只是在做 搜尋過濾分頁控制 時,例如:/search?keyword=vue&page=2

判斷方式其實只有一句:這個值拿掉後,網址還有意義嗎? 拿掉 /product/123123 就不知道要看哪個商品,它屬於 params;拿掉 ?page=2 仍然是同一個列表頁,它屬於 query。

總結

動態路由讓 Vue 應用程式能以最少的程式碼管理龐大的頁面需求。掌握了 params 的傳遞與監聽,您就能輕鬆開發出如電商平台般靈活的網頁系統!

常見問題

params 與 query 該用哪一個?

params 代表 資源本身,是網址不可或缺的一段,例如:商品編號;query 代表 同一頁的不同狀態,例如:關鍵字、排序與頁數。判斷方式是問這個值拿掉後網址還有意義嗎。

為什麼切換到另一個 id 後 onMounted 沒有再執行?

因為路徑對應的是同一個元件,Vue 會重用元件實體而不重新建立,掛載階段 只會發生一次。要在參數變化時重新取資料,請用 watch 監聽 route.params

useRoute 與 useRouter 差在哪裡?

useRoute 取得目前路由的資訊,用來讀 paramsquerymetauseRouter 取得路由器實體,用來執行 pushreplaceback 等跳轉動作。讀資料用前者,做動作用後者。

params 取到的值為什麼都是字串?

網址本質上是字串,所以 route.params.id 拿到的是字串。需要數字時請自行轉換,或在路由設定加上正則限制只接受數字。

動態參數可以是選填的嗎?

可以,在參數後加上問號寫成 /product/:id? 就變成選填,此時 /product 也會匹配到同一個元件,元件內需自行判斷參數是否存在。

延伸閱讀