規則:可以查前面的課、MDN、API 文件,也可以使用 coding agent;但不要直接複製一個完整成品。這一關驗收的是你能不能自己拆問題、找出資料流、除錯並完成作品。
你的任務:做一個 Todo Explorer
建立一個新的前端專案,從公開 API 載入 todo 資料,讓使用者可以查看、篩選、搜尋與重新載入。
資料來源可以沿用上一課的:
https://jsonplaceholder.typicode.com/todos
這不是要你做完整待辦 App,因為目前還沒有 backend 與 database。你的任務是把「外部資料 → 狀態 → 使用者操作 → 畫面更新」做完整。
先建立乾淨的新專案
不要直接在上一課練習頁上繼續堆。建立:
todo-explorer/
├─ index.html
├─ styles.css
├─ script.js
├─ README.md
└─ .gitignore
用本機 HTTP server 執行,例如:
python -m http.server 8000
或 Windows:
py -m http.server 8000
再從 http://localhost:8000 開啟。
Checkpoint A|先把 HTML 結構做好
在還沒有任何 JavaScript 前,頁面就應該有合理的語意結構。
必須有:一個主標題與簡短說明。
必須有:一個搜尋輸入框。
必須有:篩選控制:All / Active / Completed。
必須有:Reload 按鈕。
必須有:顯示 loading / error / summary 的 status 區域。
必須有:放 todo items 的列表或卡片容器。
不要因為等等 JavaScript 會動態產生內容,就把 HTML 全部縮成一個空 div。固定的頁面骨架仍然應該由 HTML 表達。
Checkpoint B|先讓 CSS 在沒有資料時也合理
頁面至少要做到:
內容寬度不會在大螢幕無限拉長。
搜尋與篩選控制在窄螢幕不會爆版。
todo item 有清楚的 completed / active 視覺差異。
按鈕與輸入框有基本 focus 狀態。
至少使用一次 Flexbox 或 Grid 做真正的 layout。
手機寬度下不出現明顯水平捲動。
這一關不是設計比賽,但不能回到「瀏覽器預設樣式全部沒整理」的狀態。
Checkpoint C|定義你的 JavaScript state
不要把資料散落在 DOM 裡。先想清楚程式需要記住什麼。
一個合理的最小 state 可能包含:
let todos = [];
let currentFilter = "all";
let searchQuery = "";
let isLoading = false;
let errorMessage = "";
你不必一模一樣照抄,但要能回答:哪個變數代表 API 原始資料?哪個代表使用者現在選的 filter?哪個代表搜尋文字?
Checkpoint D|載入資料
建立一個專門負責 request 的 async function。它至少要處理:
開始 request 前進入 loading 狀態。
使用 fetch() 發出 GET request。
檢查 response.ok。
使用 response.json() 解析資料。
成功後把資料存進 state。
失敗時顯示使用者看得懂的錯誤訊息。
finally 或等價流程中解除 loading 狀態。
建議一開始只取一小部分資料,例如:
todos.slice(0, 30)
不是因為瀏覽器不能顯示更多,而是這一關的重點是資料流,不是把 200 筆假資料全部堆到畫面上。
Checkpoint E|把 render 和 fetch 分開
不要讓一個 loadTodos() 同時負責抓資料、篩選、建立 DOM、處理搜尋、更新所有按鈕。
至少嘗試拆成類似:
loadTodos()
getVisibleTodos()
renderTodos()
renderStatus()
名稱可以不同,但職責要分開。
filter/search → 從 state 算出要顯示的資料
render → 把結果畫到 DOM
Checkpoint F|完成 Filter
使用者要可以選:
Active → completed === false
Completed → completed === true
篩選時不要重新 fetch。API 資料已經在 state 裡,這時只是改變「哪些資料要被顯示」。
也就是:
Checkpoint G|完成搜尋
搜尋框至少要依 todo 的 title 篩選。
建議把大小寫問題先處理掉:
todo.title.toLowerCase().includes(query.toLowerCase())
如果搜尋是即時更新,可以監聽 input;如果你想做成按下搜尋才執行,也可以使用 form + submit。
重點不是事件種類,而是你能解釋:
Checkpoint H|Filter 和 Search 要能一起工作
這是整關最重要的邏輯之一。
不能出現:
搜尋之後 filter 失效。
切 filter 之後搜尋文字被忘記。
每個 event handler 都自己寫一套完全不同的篩選邏輯。
比較好的思路是:每次畫面要更新時,都從完整 state 計算一次「目前真正應該顯示哪些 todos」。
Checkpoint I|Reload 要真的重新發 request
Reload 按鈕不是 location.reload()。
它應該再次執行你的資料載入函式:
Loading 期間可以暫時 disable 按鈕,避免連點造成多個 request。
Checkpoint J|Error state 不能只藏在 Console
開發者需要 Console,但使用者看不到你的 Console。
你要故意把 API URL 改錯一次,確認:
頁面真的會顯示 Error 狀態。
Reload 不會永遠卡在 disabled。
舊資料是否保留或清空,是你有意識做的決定。
Console 仍然保留足夠資訊讓開發者除錯。
測完再把 URL 改回正確版本。
Checkpoint K|DOM 建立要保持安全與可讀
API 的 title 是文字,所以顯示時使用:
element.textContent = todo.title;
不要為了方便把 API 字串直接塞進大型 innerHTML template。
這一關至少要使用 createElement、classList、append 或同等 DOM API 建立主要資料內容。
Checkpoint L|Summary 也要來自資料
頁面上顯示一段摘要,例如:
Showing 8 of 30 · 12 completed
數字不能寫死。要由目前 state 與 visible data 計算出來。
這會逼你再次分清楚:
至少使用一次 DevTools Network 來除錯
在 Network 面板重新載入資料,確認你能找到這次 API request。
至少看過:
Request URL。
Status code。
Response / Preview。
Request 發生的時間點。
如果畫面沒有資料,先判斷是「request 根本沒成功」還是「資料到了,但 render 壞了」。
可以用 AI coding agent,但要留下理解
Codex、Claude Code、AGY 都可以幫你做這個專案,但至少遵守三件事:
在接受變更前看 diff。
它新增函式時,你要能說出函式的 input / output / responsibility。
它改動 fetch、state 或 render 流程時,你要重新測 loading / success / error 三種狀態。
如果 agent 一次產生 300 行而你完全不知道資料怎麼流,這一關就還沒有完成。
Checkpoint M|用 Git 留下可讀歷史
這不是 Start Here 的 Git 重考,但既然現在是真正的小專案,就不要回到「最後全部一次 commit」。
至少留下三個有意義的 commits,例如分成:
加入 API 載入與 render
加入搜尋、filter 與錯誤狀態
訊息自己寫。完成後推到 GitHub。
加分,但不是完成條件
如果核心功能都穩定,可以再選一兩個:
顯示目前 active filter 的視覺狀態。
加入 Empty state,例如「沒有符合搜尋條件的資料」。
加入簡單排序。
把 URL / selectors / render helpers 整理得更乾淨。
顯示最後成功載入時間。
不要在核心功能還壞掉時先做動畫、漸層或十個花俏功能。
最終驗收
□ HTML 有語意結構,不是只有一個空容器。
□ CSS 在桌機與手機寬度都能正常閱讀。
□ JavaScript 有明確 state,不依賴 DOM 當唯一資料來源。
□ 我能成功 fetch API,並檢查 response.ok。
□ Loading / Success / Error 三種狀態都測過。
□ API array 能被 render 成多個 DOM elements。
□ Search 與 All / Active / Completed filter 可以一起使用。
□ Reload 真的會重新發出 request。
□ 我用過 DevTools Console、Elements、Network 除錯。
□ 使用者看得到錯誤,而不是只有 Console 有訊息。
□ 我沒有把任何 secret / API key 放在前端。
□ Git history 至少有三個有意義的 commits,而且已推到 GitHub。
如果全部做到,你現在會的是什麼?
你還沒有學 framework,也還沒有 backend。但你已經具備完整的前端基本資料流:
↓
CSS presentation
↓
JavaScript state / logic
↓
Events 接收使用者操作
↓
Fetch 取得外部資料
↓
DOM render 畫面
這比「會抄 React component」更重要,因為接下來任何 framework 都只是在這些概念上提供新的組織方式。
下一條路徑是 Web App:前端不再只讀別人的 API,而是開始建立自己的 backend、database、authentication 與部署流程。
Web Basics 完成條件:你能在不依賴前端 framework 的情況下,從零做出一個 responsive、可互動、會呼叫 API、會處理 loading / error、並能把資料安全渲染到 DOM 的小網站。