---
# System prepended metadata

title: 第二週：Vue Composition API，方法、監聽與 AJAX
tags: [2025 vue3 新手營]

---

:::info
本文件連結：https://hackmd.io/3nx1b0V3RV-KCNHGWftH_g
此為 2025 Vue3 前端新手營課程資料，相關資源僅授權給該課程學員。
:::

# 第二週：Vue Composition API，方法、監聽與 AJAX

## 課前說明

- 每日任務：Discord 上發布
- 每週挑戰：
    - 一～三週：HackMD 上繳交
    - 最後一週作業
- RPG 程式勇者：
    - 課程捷徑
    - 課程講義
    - 最後一週作業繳交
- 提醒老師錄影


## 課綱核心

- 複習，指令
- Methods、Computed、Mounted 常見技巧
- Async Function
## 上半場要做的架構

![截圖 2025-08-06 上午11.37.14](https://hackmd.io/_uploads/ByKx0SeOgx.png)

## 建立環境

```
npm create vite@latest
```

## 複習與指令

完成一個 CRUD 的範例

## Mounted、Computed
![upload_a90e14bc11cbd94813a9c5366f538aae](https://hackmd.io/_uploads/B1LI_rxOge.png)

### Mounted
![截圖 2025-08-06 上午11.31.19](https://hackmd.io/_uploads/BJt2nrlOel.png)

- 關於生命週期：https://cn.vuejs.org/guide/essentials/lifecycle.html
- 關於 Mounted 來說：
    - 最常用到的生命週期
    - 可以作為**初始化事件**（例如 Ajax 取得第一筆資料）
    - 在此生命週期後，才能抓到 DOM 元素
    - 可以重複使用

### Computed

- 自動監聽
- 可以產出一個新的結果，僅能讀取，不能寫入
- 主要用途為顯示在畫面上（HTML）
- 不能寫入其他的值
![截圖 2025-08-06 上午11.43.02](https://hackmd.io/_uploads/r1DF18eOel.png)

除此之外，還有…

- Watch
- WatchEffect 等方法

## Async Function

本課程會使用 Axios 套件進行呼叫

1. 為什麼要用 Promise、Async/Await（本課程都是以 Async/Await 為主
2. Async/Await 實戰運用方法
3. Async function 的錯誤處理
4. Vue 中使用 Async Function
    1. onMounted 中使用
    2. 主動呼叫

## 課程 API 串接

API 文件：https://todolist-api.hexschool.io/doc/

Cookie 文件：https://developer.mozilla.org/zh-CN/docs/Web/API/Document/cookie
![vue-router-architecture](https://hackmd.io/_uploads/ByP7G_edge.svg)

## 作業
![截圖 2025-08-06 下午2.11.11](https://hackmd.io/_uploads/rkOnQ5xdgg.png)

Todo List API

參考課程 API，完成 TodoList 串接：https://todolist-api.hexschool.io/doc/#/

- Level 1：完成所有功能的串接
- Level 2：嘗試套版，版型不限（單頁完成）

解答連結：

- 執行範例：https://www.casper.tw/2024-vue-homework/#/week2
- 原始碼：https://github.com/Wcc723/2024-vue-homework/blob/main/src/views/Week2View.vue
- 作業繳交連結：https://hackmd.io/GaHyBZ0nR-6-Z7s6zbXQhA

### 預告

最終任務：

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


### 上半場範例

::: spoiler
```
<template>
<h1>複習</h1>

<div>
    <input type="text" v-model="newName">
    {{ newName }}
    <input type="text" v-model="newNumber">
    {{ newNumber }}
    <button type="button" @click="addProduct">
        新增到資料集裡面
    </button>
</div>
<table>
    <thead>
        <tr>
            <th>標題</th>
            <th>價格</th>
            <th>調整價格</th>
            <th>刪除</th>
        </tr>
    </thead>
    <tbody>
        <tr v-for="item in data" :key="item.id">
            <td>{{ item.name }}</td>
            <td>{{ item.price }}</td>
            <td><input type="number" v-model="item.price"></td>
            <td>
                <button type="button" @click="delItem(item.id)">刪除品項</button>
            </td>
        </tr>
    </tbody>
</table>
</template>

<script setup>

import { ref } from "vue";

const newName = ref('');
const newNumber = ref(0);
const data = ref([
  { id: 1, name: "珍珠奶茶", price: 50 },
  { id: 2, name: "冬瓜檸檬", price: 45 },
  { id: 3, name: "翡翠檸檬", price: 55 },
  { id: 4, name: "四季春茶", price: 45 },
  { id: 5, name: "阿薩姆奶茶", price: 50 },
  { id: 6, name: "檸檬冰茶", price: 45 },
  { id: 7, name: "芒果綠茶", price: 55 },
  { id: 8, name: "抹茶拿鐵", price: 60 }
])
const addProduct= () =>{
    console.log("addProduct")
    data.value.push({
        id: new Date().getTime(),
        name: newName.value,
        price: newNumber.value
    })
    newName.value='';
    newNumber.value=0;
}
const delItem =(id) =>{
    console.log('delItem',id);
    const index = data.value.findIndex(item=>item.id===id);
    data.value.splice(index,1);
}
</script>

<style>
</style>
```

## todo

```

<template>
<h1>待辦</h1>
<h2>註冊功能</h2>
<input type="email" placeholder="Email" v-model="signupField.email">
<input type="text"  placeholder="密碼"  v-model="signupField.password">
<input type="text"  placeholder="暱稱"  v-model="signupField.nickname">
<br>
{{ signupField }}
<br>
<button type="button" @click="signup">註冊</button>
{{ signupRes }}


<h2>登入功能</h2>
<input type="email" placeholder="Email" v-model="signInField.email">
<input type="text"  placeholder="密碼"  v-model="signInField.password">
<br>
{{ signInField }}
<br>
<button type="button" @click="signIn">登入</button>
<br>
token:{{ signInRes }}

<h2>驗證</h2>
<div v-if="user.uid">
    <p>UID:{{ user.uid }}</p>
    <p>NickName:{{ user.nickname }}</p>
</div>
<div v-else>
    你還沒有登入
</div>
</template>

<script setup>
import {ref,onMounted} from 'vue';
import axios from 'axios';
const api = 'https://todolist-api.hexschool.io/';
const signupField = ref({
    email:'',
    password:'',
    nickname:''
})
const signupRes = ref('')
const signup = async()=>{
   try{
     const res = await axios.post(`${api}users/sign_up`,signupField.value);
        console.log(res);
        signupRes.value = res.data.uid

   }catch(error){
        console.log("錯誤！")
        console.log(error)
   }
}
const signInField = ref({
    email:'',
    password:''
})
const signInRes = ref('');
const signIn = async()=>{
   try{
     const res = await axios.post(`${api}users/sign_in`,signInField.value);
        console.log(res);
        signInRes.value = res.data.token;
        document.cookie = `customTodoToken=${res.data.token};path=/`

   }catch(error){
        console.log("錯誤！")
        console.log(error)
   }
}

// 驗證

const user = ref({
    nickname: '',
    uid:''
})

onMounted(async()=>{
    // 驗證登入
    const token = document.cookie.replace(/(?:^|.*;\s*)customTodoToken\s*=\s*([^;]*).*$/i,
    "$1")
    const res = await axios.get(`${api}users/checkout`,{
        headers:{
            Authorization: token
        }
    })
    console.log(res);
    user.value= res.data;
})

</script>

<style></style>
```

:::