---
# System prepended metadata

title: 第四週
tags: [2024 Vue 前端新手營 - Composition API]

---

# 第四週

- 課前影音 / 錄影回放：https://courses.hexschool.com/courses/enrolled/2574732
- 每日任務：Discord 上發布
- 討論頻道：https://discord.com/channels/801807326054055996/1258601572867379222
- 每週挑戰：
    - 一～三週：HackMD 上繳交
    - 最後一週作業：https://rpg.hexschool.com/#/training/12062289980134006991
- RPG 程式勇者：
    - 課程捷徑
    - 課程講義
    - 最後一週作業繳交
- 提醒老師錄影

### 額外課程介紹：
- JS 直播班：https://www.hexschool.com/courses/js-training.html
- JS&React 直播班：https://www.hexschool.com/courses/js-react-training.html
    - 今晚開放報名，限量 60 位，剩餘 28 位


## 今日主要主題

- Router
- Pinia

### Router

- 路由是什麼（路由表概念）
![image](https://hackmd.io/_uploads/rkuCwZQj0.png)


- 路由結構配置 → 基本頁面結構配置
    - 如何配置 router 檔案
    - router-view
    - router-link 連結方式

## 路由設定要點：

原本是使用 createWebHistory，當選擇此模式會使得無法部署至 GitHub Pages

如果有辦法調整 Server 環境，則可以參考此[文件](https://router.vuejs.org/zh/guide/essentials/history-mode.html#HTML5-%E6%A8%A1%E5%BC%8F)的方法。

```jsx
import { createRouter, createWebHistory } from 'vue-router'

const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  ...
})
```

如果要部署至 GitHub Pages，則可以建議換成 **createWebHashHistory** 的模式。

```jsx
import { createRouter, createWebHashHistory } from 'vue-router'

const router = createRouter({
  history: createWebHashHistory(import.meta.env.BASE_URL),
  ...
})
```

## 路由基本手法

1. 開新頁面 XxxView.vue
2. 撰寫路由表 router/index.js
3. 提供連結方法 routerLink

## 常見路由結構

1. 基本路由
2. 巢狀路由 → 主要用為版型重複運用
    - 巢狀路由 → 巢狀連結
    
router/index.js
```jsx
{
    path: '/nest-router',
    component: () => import('../views/NestRouter.vue'),
    children: [
      // ...
    ]
  }
```

vue file
```jsx
<template>
  <h1>巢狀路由</h1>

  <RouterView />
</template>
```
    
3. 動態路由（本次作業不會用到，但業界常見）
    - 動態路由 → 動態參數

router/index.js

```jsx
{
  path: '/:id',
  component: () => import('../views/DynamicRouter.vue')
}
```

vue file

```jsx
<script setup>
import { useRoute } from 'vue-router'

const route = useRoute()
console.log('router:', route);
const { id } = route.params; // 網址上的值
</script>
```
    

## 路由方法與狀態

- 路由狀態 useRoute
    - [https://router.vuejs.org/zh/guide/essentials/dynamic-matching.html#响应路由参数的变化](https://router.vuejs.org/zh/guide/essentials/dynamic-matching.html#%E5%93%8D%E5%BA%94%E8%B7%AF%E7%94%B1%E5%8F%82%E6%95%B0%E7%9A%84%E5%8F%98%E5%8C%96)
- 路由方法 useRouter
    - [https://router.vuejs.org/zh/guide/advanced/composition-api.html#在-setup-中访问路由和当前路由](https://router.vuejs.org/zh/guide/advanced/composition-api.html#%E5%9C%A8-setup-%E4%B8%AD%E8%AE%BF%E9%97%AE%E8%B7%AF%E7%94%B1%E5%92%8C%E5%BD%93%E5%89%8D%E8%B7%AF%E7%94%B1)
    - 方法轉頁
    - 404
    
    ```jsx
    { path: '/:pathMatch(.*)*', name: 'NotFound', component: NotFound },
    ```
    

- router.push(), back()
- default link

```
{
 path: '*',
redirect: '/',
},

```


## 定義自己的路由配置

1. 請先定好完整路由架構
2. 將路由配置對應所有頁面，開發順序：
    1. 先開頁面檔案
    2. 撰寫路由配置
    3. 補上 router-link
- 登入頁面 /login (不驗證)
- 前端頁面 (需驗證)
    - todo


## 補充：pathMatch

404 頁面：https://router.vuejs.org/guide/essentials/dynamic-matching#Catch-all-404-Not-found-Route

## Pinia

### 環境

環境起手式

```jsx
npm install pinia
```

在 main.js 中加入以下

```jsx
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'

const pinia = createPinia()
const app = createApp(App)

app.use(pinia)
app.mount('#app')
```

官方提醒：https://pinia.vuejs.org/zh/getting-started.html#when-should-i-use-a-store

### 引入 Store

1. 建立儲存空間

建立檔案 `stores/xxxx.js` 開始建立一個 store，store 撰寫方式與 composition API 無異。

```jsx
import { ref, computed } from 'vue'
import { defineStore } from 'pinia'

export const useCounterStore = defineStore('counter', () => {
  const count = ref(0)
  const doubleCount = computed(() => count.value * 2)
  function increment() {
    count.value++
  }

  return { count, doubleCount, increment }
})
```

1. 在元件中引入 Store（完成）

```jsx
<script setup>
import { useCounterStore } from './store/countStore';
const countStore = useCounterStore()
</script>
<template>
<p>{{ countStore.count }} / {{ countStore.doubleCount }}</p>
<button @click="countStore.increment">++</button>
</template>
```

1. 解構屬性

```jsx
<script setup>
import { useCounterStore } from '@/store/countStore';
import { storeToRefs } from 'pinia';

const counterStore = useCounterStore()
// 非函式的值需要使用 storeToRefs
const { count, doubleCount } = storeToRefs(counterStore)

// 函式則不需要
const { increment } = counterStore
</script>
<template>
  <p>{{ count }} / {{ doubleCount }}</p>
  <button @click="increment">++</button>
</template>
```

1. 在 Pinia Store 中導入其他 Composition API 方法

```jsx
import { useRouter } from 'vue-router'

export const useCounterStore = defineStore('counter', () => {
  const router = useRouter()

  function xxxx() {
    router.push('/')
  }

  return { xxxx }
})

```

## 最終任務：

- 設計稿：https://www.figma.com/design/MFSk8P5jmmC2ns9V9YeCzM/TodoList?node-id=0-1&t=KzEgeE3s3r6JIPh6-1
- CSS 範例：https://codepen.io/hexschool/pen/qBzEMdm
- 同時挑戰證書任務：
    - [最終挑戰 - Todolist 新手證書任務](https://rpg.hexschool.com/#/tasks/12062289980169123704?preview=1)
    - [最終挑戰 - Todolist API 整合證書任務](https://rpg.hexschool.com/#/tasks/12062289980139618730?preview=1)

> 提醒：
> 最終證書作業繳交截止日：9/8（日）
完課證書發送時間：9/20（五）前
影音觀看期限：直播錄影將為您保留半年，期限為 2025/03/31