Skip to content

Vue Router 巢狀路由:打造高效的頁面版面配置與局部切換機制

Vue Router 巢狀路由:children 與 router-view

在開發複雜的網頁應用時,我們經常會遇到 頁面外殼不變,僅局部內容切換 的需求,例如:會員中心、後台管理系統。這時 巢狀路由 (Nested Routes) 就是您的最佳工具。

為什麼需要巢狀路由?

巢狀路由允許我們在父路由元件內部定義子路由,並在特定位置渲染子頁面。

  • 程式碼複用:不需要在每個子頁面重複撰寫相同的導覽列、側邊欄或頁尾。
  • 狀態保持:切換子路由,例如:從 個人資料 切換到 訂單列表 )時,父元件(外殼)不會被銷毀重洗,能保持原有的輸入狀態或滾動位置。

規劃檔案架構

會員中心 為實體,我們將頁面拆分為一個父元件與兩個子元件:

src/
 ├─ router/
 │   └─ index.js            # 路由核心配置
 ├─ views/
 │   ├─ member/             # 存放子頁面
 │   │   ├─ Profile.vue     # 子頁面:個人資料
 │   │   └─ Orders.vue   # 子頁面:訂單列表
 │   ├─ Member.vue          # 父頁面:外殼
 │   └─ ...
 ├─ App.vue
 └─ main.js

父元件配置:準備 <router-view />

父元件 Member.vue 扮演的是 Layout(版面配置)的角色,它決定了子頁面要顯示在哪裡。

Member.vue

vue
<template>
  <h1>會員中心</h1>
  <router-link to="/member/profile">個人資料</router-link>|
  <router-link to="/member/orders">訂單列表</router-link>
  <RouterView />
</template>

子頁面沒出現?先檢查這裡

巢狀路由最常見的問題就是 父元件忘了放 <router-view />。子路由的元件是渲染在父元件的 <router-view /> 位置,父層沒有這個標籤,路由雖然切換成功,畫面上卻什麼都不會變。

此時整個應用程式會有 兩層 <router-view />App.vue 那一層負責顯示 Member.vueMember.vue 這一層負責顯示 Profile.vueOrders.vue

子元件配置

這兩個元件會被渲染在父頁面的 <router-view /> 位置。

Profile.vue

vue
<template>
  <h3>會員個人資料</h3>
</template>

Orders.vue

vue
<template>
  <h3>訂單歷史紀錄</h3>
</template>

路由表設定:使用 children 屬性

在路由設定檔中,我們利用 children 陣列來定義層級關係。注意:子路由的 path 通常不需要加斜線 /。如果加了 / 會被視為從根目錄開始。

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',
      name: 'product',
      component: () => import('@/views/Product.vue'),
    },
    {
      path: '/member',
      component: () => import('@/views/Member.vue'),
      children: [
        {
          // 當路徑為 /member 時,自動重新導向至 profile
          path: '',
          name: 'member',
          redirect: '/member/profile',
        },
        {
          // 注意:子路由 path 通常不加 /,會自動串接在父層之後
          path: 'profile',
          name: 'member-profile',
          component: () => import('@/views/member/Profile.vue'),
        },
        {
          // 注意:子路由 path 通常不加 /,會自動串接在父層之後
          path: 'orders',
          name: 'member-orders',
          component: () => import('@/views/member/Orders.vue'),
        },
      ],
    },
    {
      path: '/:pathMatch(.*)*',
      redirect: '/',
    },
  ],
});

關鍵規則與開發細節

子路由 Path 的斜線問題

  • path: 'profile' (不加斜線):網址會自動拼接為 /member/profile
  • path: '/profile' (加斜線):會被視為 根路徑,網址會變成 /profile 而非 /member/profile,雖然它依然是 Member.vue 的子路由。建議子層路徑統一 不加斜線 以保持結構語意。

預設子頁面的兩種寫法

進入 /member 時總得顯示點東西,常見有兩種做法:

js
// 寫法一:導向指定的子路由(網址會變成 /member/profile)
{ path: '', redirect: '/member/profile' }

// 寫法二:直接指定預設元件(網址維持 /member)
{ path: '', name: 'member', component: () => import('@/views/member/Profile.vue') }

想讓網址明確反映目前分頁就用第一種;希望網址保持簡短則用第二種。

具名路由的建議

通常在開發巢狀路由時,建議把 name: 'member' 從父層拿掉或移到子層,因為訪問 /member 時,它會立刻跳轉到 /member/profile,這時父層的 name 其實很少被直接用到。亦可移除因為導向父路由名稱時,系統不確定要停在父層還是直接進入預設子層的警告。

父元件不會被重建

這是巢狀路由最實際的好處:子路由切換時,父元件不會被銷毀,所以側邊欄的展開狀態、捲軸位置與已經取回的資料都會保留。若父元件在 onMounted 抓過會員基本資料,切換分頁時不會重複請求。

搭配權限控管

會員中心這類頁面通常需要登入才能進入。由於 to.meta自動合併父層與子層,只要在父路由加上一次標記,底下所有子頁面都會受到保護:

js
{
  path: '/member',
  component: () => import('@/views/Member.vue'),
  meta: { requiresAuth: true },
  children: [ /* 略 */ ],
}

完整的驗證流程請看 導航守衛。若多個子頁面需要共用會員資料,則適合交給 Pinia 保存。

總結

巢狀路由是 Vue 開發中處理 Layout 版面配置的核心。透過 children 配置與 <router-view /> 的嵌套,您能輕鬆構建出層級分明、易於維護的 SPA 應用程式。

常見問題

子路由的 path 要不要加斜線?

不要加。寫 profile 會自動串接成 /member/profile;寫成 /profile 會被當成根路徑,網址變成 /profile。建議子層統一不加斜線以保持層級語意。

為什麼子頁面沒有顯示出來?

最常見的原因是父元件忘記放 <router-view />。子路由的元件是渲染在父元件的 <router-view /> 位置,父層沒有這個標籤就沒有地方顯示。

進入父路由時要怎麼顯示預設子頁面?

children 裡加一筆 path 為空字串的路由,用 redirect 導向想要的子頁面,或直接指定 component 當作預設頁。

切換子路由時父元件會被重建嗎?

不會。父元件會保留在畫面上,因此側邊欄的展開狀態、捲軸位置與已載入的資料都不會重置,這也是巢狀路由最大的好處。

巢狀路由可以做幾層?

層數沒有硬性限制,只要每一層的元件都放了 <router-view /> 就能繼續往下嵌套。但層級過深會讓路由表難以閱讀,實務上多數專案兩到三層就足夠。

延伸閱讀