---
# System prepended metadata

title: "\U0001F3C5 Day 7 - 嵌套式路由、 useRouter & useRoute"
tags: [2024 Nuxt 直播班]

---

# 🏅 Day 7 - 嵌套式路由、 useRouter & useRoute

## 今日學習目標

- 學習建立並使用嵌套式路由
- 學習在 Nuxt3 使用 useRouter 和  useRoute

## 前言

在 Day6 中，我們已學習了基礎路由結構，並提及了如下圖所示的結構。圖中使用的 product 資料夾就屬於嵌套式路由的觀念。透過這個資料夾結構，我們可以在 `/product` 底下進一步實作子頁面，協助我們在管理多個子頁面的同時保持清晰的路由結構 。

![image](https://hackmd.io/_uploads/rJM5twnbJe.png)

另外，圖中的 `/product/[id].vue` 檔案為動態路由，用於處理像 `/product/1` 或 `/product/2` 這類動態生成的路徑。動態路由會在後續的學習再進行探討，今天的目標將集中在嵌套式路由與 `useRouter` 、 `useRoute` 的實作。

## 嵌套式路由

當頁面路由有多層級的頁面，例如一個主頁面下包含多個子頁面時，可以使用嵌套式路由在主要路由下建立多個子路由，以建立更直觀、清晰的 URL 結構。除此之外，若所有子頁面都有共用的區塊，可以在主頁面放入這些共用的元件，避免程式碼的重複。

關於在 Nuxt3 使用嵌套式路由可以閱讀 [官方文件](https://nuxt.com/docs/guide/directory-structure/pages#nested-routes)  。

### 建立步驟

以下將繼續使用 `product` 路由為例來實作嵌套式路由，但我們會將動態路由 `[id].vue` 替換成靜態的產品內頁頁面 `productA.vue` 與  `productB.vue`，避免同學混淆，如下圖所示 :

![image](https://hackmd.io/_uploads/SknjKDnZJe.png)

步驟一. 在  `pages` 資料夾新增建立  `product.vue`  作為主要的產品頁面，後續步驟將作為嵌套路由的主容器。

```html
<!-- pages/product.vue -->
<script setup></script>

<template>
  <div class="container">
    <h1>產品頁面</h1>
  </div>
</template>

<style scoped>
.container {
  max-width: 1320px;
  margin: 0 auto;
}
</style>
```

步驟二.  在  `pages` 資料夾內建立一個名為 `product` 的資料夾。這個資料夾將用來放置產品頁面的子路由，例如產品內頁和產品列表頁。

```jsx
pages/
|-- product/
|  
|-- product.vue
|-- index.vue
```

步驟三. 在`product` 資料夾內新增子頁面 `productA.vue` 與  `productB.vue` ，作為要在產品頁面下實作的嵌套式路由。

```jsx
pages/
|-- product/
|   |-- productA.vue
|   |-- productB.vue
| 
|-- product.vue
|-- index.vue
```

```jsx
pages/product/productA.vue
<script setup></script>

<template>
  <h2>產品內頁 productA</h2>
</template>

<style scoped></style>
```

```html
<!-- pages/product/productB.vue -->
<script setup></script>

<template>
  <h2>產品內頁 productB</h2>
</template>

<style scoped></style>
```

步驟四. 在  `product` 資料夾內新增子頁面 `index.vue` 作為產品主列表頁面，當進入 `/product/` 路徑時，這個頁面將作為 `/product/` 路徑的預設子頁面，顯示產品的主列表。

```jsx
pages/
|-- product/
|   |-- productA.vue
|   |-- productB.vue
|   |-- index.vue
|   
|-- product.vue
|-- index.vue
```

```html
<!-- pages/product/index.vue -->
<script setup></script>

<template>
  <h2>產品主列表</h2>
</template>

<style scoped></style>
```

步驟五. 最後，需要回到 `pages/product.vue` 使用  `<NuxtPage />` 來顯示嵌套式路由頁面的內容，並加入 `<NuxtLink>` 元件提供導航連結。當切換到 `/product/productA` 或 `/product/productB` 時，對應的頁面內容將被渲染到 `<NuxtPage />` 元件所在的位置。

另外在這一個頁面中有加入產品列表 `.product-category` ，這一區塊將作為所有子頁面共用的區塊，在切換到 `/product/productA` 和 `/product/productB`  都會被渲染。

```html
<!-- pages/product.vue -->
<script setup></script>

<template>
  <div class="container">
    <h1>產品頁面</h1>
    <div class="product">
      <div class="product-category">
        <h2>產品列表</h2>
        <ul>
          <li>
            <NuxtLink to="/product/">全部產品</NuxtLink>
          </li>
          <li>
            <NuxtLink to="/product/productA">productA</NuxtLink>
          </li>
          <li>
            <NuxtLink to="/product/productB">productB</NuxtLink>
          </li>
        </ul>
      </div>
      <main class="product-list">
        <NuxtPage />
        <!-- 顯示 /product/ 下的頁面 -->
      </main>
    </div>
  </div>
</template>

<style lang="scss" scoped>
.product {
  display: flex;
  gap: 1rem;
  &-category {
    width: 30%;
    outline: 1px solid #000;
    padding: 0 20px;
    ul {
      padding: 0;
      list-style: none;
    }
  }
  &-list {
    width: 70%;
    outline: 1px solid #000;
  }
}
</style>
```

## 在 Nuxt3 使用路由方法與屬性

Nuxt3 將 `vue-router` 的功能整合進 Composables，其運作行為與  `vue-router` 相同。因此在 `pages/` 資料夾**的**頁面元件中可以使用 Composition API 的路由寫法 `useRouter()` 與 `useRoute()` 來使用路由。

### useRoute

`useRoute` 用於取得目前路由的詳細資訊，包括路由參數、查詢參數、路由名稱等。以上方範例的  `pages/product/productA.vue` 為例，以下使用 `useRoute` 來獲取不同的路由資訊：

```html
<!-- pages/product/productA.vue -->
<script setup>
// Auto Imports 
const route = useRoute();

// 獲取路由中的查詢參數
// 例如 : 路由路徑為 /product/productA ， route.query 為空物件
// 例如 : 路由路徑為 /product/productA?q=產品名稱 ， route.query 為 { q:'產品名稱' }
console.log(route.query);

// 獲取包含路由路徑 ( path ) 、查詢參數 ( query ) 和 hash 的完整 URL 
console.log(route.fullPath);

// 獲取路由的唯一名稱 ( name ) ，會以 pages 資料夾結構來命名，以 productA.vue 來說會是  product-productA
console.log(route.name);
</script>

<template>
  <h2>產品內頁 productA</h2>
</template>

<style scoped></style>
```

其他關於 `useRoute` 屬性的內容可以閱讀  [Nuxt3 文件](https://nuxt.com/docs/api/composables/use-route) 以及  [vue-router 文件](https://router.vuejs.org/api/interfaces/RouteLocationNormalizedLoadedTyped.html#Properties)

### useRouter

`useRouter` 提供操作路由的方法，包括導航到不同的頁面、替換當前路由等。以上方範例的  `pages/index.vue` 為例，以下使用 `useRouter` 的方法搭配 `<button>` 按鈕的點擊來替換當前路由。

```html
<!-- pages/index.vue -->
<script setup>
// Auto Imports
const router = useRouter();
</script>

<template>
  <div class="container">
    <h1>前台首頁</h1>
    <NuxtLink to="/product/">前往產品列表</NuxtLink>
  </div>
  <div class="container">
    <!-- 在換頁的同時調用 history.pushState() 將 URL /product/productA 加入到瀏覽器的歷史紀錄中  -->
    <!-- history.pushState() : https://developer.mozilla.org/en-US/docs/Web/API/History/pushState -->
    <button type="button" @click="router.push('/product/productA')">
      使用 router.push() 換頁
    </button>
    <!-- 在換頁的同時調用 history.replaceState() 將瀏覽器最後一筆的歷史紀錄修改成  /product/productB -->
    <!--history.replaceState() : https://developer.mozilla.org/zh-CN/docs/Web/API/History/replaceState -->
    <button type="button" @click="router.replace('/product/productB')">
      使用 router.replace() 換頁
    </button>
    <!-- 在換頁的同時調用 history.go() ，根據傳入的參數在瀏覽器歷史紀錄中移動，傳入 -1 移動到前一頁， 1 則移動到下一頁 -->
    <button type="button" @click="router.go(1)">使用 router.go(1) 換頁</button>
  </div>
</template>

<style scoped></style>
```

其他關於 `useRouter` 屬性的內容可以閱讀  [Nuxt3 文件](https://nuxt.com/docs/api/composables/use-router) 以及  [vue-router 文件](https://router.vuejs.org/api/interfaces/Router.html#Methods)


<br>

> 今日學習的[範例 Code - 資料夾: day7-nested-router-example](https://github.com/hexschool/nuxt-daily-tasks-2024) 

## 題目

請 fork 這一份[模板](https://github.com/jasonlu0525/nuxt3-live-question/tree/day7-nested-router) ，將 `/pages/room.vue` 改為嵌套式路由，並實作房型列表與房型詳細頁面（不包含動態路由） 

- 房型列表頁面的 URL 需對應 `/room/`，在此頁面使用 ES6 Fetch 或 axios 串接 [前台房型 API](https://nuxr3.zeabur.app/swagger/#/Rooms%20-%20%E6%88%BF%E5%9E%8B)  ，將資料寫入 `roomList` 變數  ，並在模板的 v-for 使用 roomList  渲染資料。
- 承上，模板的 HTML 、CSS 已有在 `/pages/room.vue`  中提供，需將其移至房型列表頁面並補上 API 串接的 JavaScript 。
- 房型詳細頁面的 URL 需對應 `/room/_id` ，在此頁顯示 “房型詳細頁面” h2 標題。
- 房型列表頁面的列表渲染之後，經點擊可以換頁至 `/room/_id`  。

> ❗需注意 :  `/room/_id`  的 `/_id`  是靜態路由，非動態路由，請建立名稱為`_id.vue` 的檔案。


- 在房型詳細頁面中提供一個「回上一頁」的按鈕，點擊後可以使用 router 方法返回 `/room/` 的房型列表頁面。
- 確保房型頁面的巢狀路由內容能正確顯示。


## 回報流程

將答案上傳至 GitHub 並複製 GitHub  repo 連結貼至底下回報就算完成了喔 !
解答位置請參考下圖（需打開程式碼的部分觀看）

![](https://i.imgur.com/vftL5i0.png)

<!--   

解答: https://github.com/jasonlu0525/nuxt3-live-answer/tree/day7-nested-router

-->




回報區
---
| #   | Discord | Github / 答案 |
| --- | ----- | ----- |
| 1 | Jim Lin | [GitHub](https://github.com/junhoulin/Nuxt3-hw-day7) |
| 2 | 眼睛 | [GitHub](https://github.com/Thrizzacode/nuxt3-live-question/tree/day7-nested-router) |
| 3 | Steven | [GitHub](https://github.com/y7516552/nuxt3-live-question/tree/day7) |
| 4 | MY | [GitHub](https://github.com/ahmomoz/nuxt3-live-question/tree/day7-nested-router-hw) |
| 5 | dragon | [GitHub](https://github.com/peterlife0617/2024-nuxt-training-homework01/tree/feature/day07) |
| 6 | Ming | [GitHub](https://github.com/ming77712/2024-nuxt3-daily05) |
| 7 | 松鼠 | [Github](https://github.com/meishinro/dailyMissionSeven) |
| 8 | Rocky | [Github](https://github.com/WuRocky/day-7-useRouter-useRoute)|
| 9 | LinaChen | [Github](https://github.com/Lina-SHU/nuxt3-live-question)|
| 10 | Eden |[Github](https://github.com/1denx/NuxtDailyHw/tree/feature/day7-nested-router)|
| 11 | Tough life | [GitHub](https://github.com/hakuei0115/Nuxt_testing) |
| 12 | NeiL | [GitHub](https://github.com/Neil10241126/nuxt-demo/tree/day7-nested-router) | 
| 13 | Stan | [GitHub](https://github.com/haoxiang16/nuxt3-live-question/tree/day7-nested-router) | 
| 14 | Ariel | [GitHub](https://github.com/Ariel0508/nuxtday6/tree/nuxtday7) | 
| 15| 小木馬 | [GitHub](https://github.com/wern0531/Nuxt-test-day6) | 
| 16 | hsin yu | [GitHub](https://github.com/dogwantfly/nuxt3-daily-task-live-question/tree/day7-nested-router) | 
| 17 | wei_Rio | [GitHub](https://github.com/wei-1539/nuxtDaily7) | 
| 18 | hannahTW | [GitHub](https://github.com/hangineer/nuxt3-live-question/tree/day7-nested-router) | 
| 19 | runweiting | [GitHub](https://github.com/runweiting/2024-NUXT-Task/tree/day7) | 
| 20 | tanuki狸 | [GitHub](https://github.com/tanukili/Nuxt-2024-week01-2/tree/day7-nested-router) | 
| 21 | Fabio20 | [GitHub](https://github.com/fabio7621/nuxt3-live-question-d2/tree/nuxt3-live-day7) | 
| 22 | barry1104 | [GitHub](https://github.com/barrychen1104/nuxt3-live-question/tree/day7-nested-router) | 
| 23 | 小蹦 | [GitHub](https://github.com/lgk559/hex-Nuxt3/tree/day7-nested-router) |
| 24 | Nielsen | [GitHub](https://github.com/asz8621/nuxt3-daily-task/tree/day7-nested-router) | 
| 25 | 阿塔 | [GitHub](https://github.com/QuantumParrot/2024-Hexschool-Nuxt-Camp-Daily-Task) | 
| 26 | Johnson | [GitHub](https://github.com/tttom3669/2024_hex_nuxt_daily/tree/day7-nested-router) | 
| 27 | lidelin | [GitHub](https://github.com/Lide/nuxt3-live-question/tree/day7-nested-router) | 
| 28 | bb | [GitHub](https://github.com/wicebing/nuxt3-live-question/tree/day7-nested-router) | 

<!-- 快速複製
 | --- | ----- | [GitHub]() | 
-->