:::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> ``` :::