MIXXIN Vibe Coder 入門課
LESSON 04 · 午餐揪團頁主線

讓資料留得住,再抓外部真實資料

第 3 堂結尾你撞到一個怪:把「我要跟」的人數點高,一重整就全部歸零。今天第一件事,就是用一個叫 localStorage 的「抽屜」把它收拾掉——重整之後,你加的東西還在。收拾完這個痛,我們再往外走一步:讓頁面抓「這一刻真實世界正在發生」的資料。

localStorage 抽屜 重整還在 fetch 抓外部資料 產出:留得住 + 抓得到
完成定義 · Definition of Done
學員重整頁面後,「我要跟」的狀態還在(存進了 localStorage);頁面上有一個資料是「抓外部真實資料」顯示出來的,學員能在 Network 面板中指認出對應的請求;並且頁面接上了全班共用的地點清單——自己頁面顯示的地點總數跟同學的一致(大家拉的是同一份)。
01localStorage:讓重整不再歸零

還記得第 3 堂那個怪嗎?把人數點高,一重新整理就全部歸零——因為那個數字只是「這次打開頁面的短期記憶」,沒被記到任何比較持久的地方。今天就來收拾它。

想像瀏覽器裡有一個抽屜:你把東西放進去,就算把頁面關掉重開,抽屜還在原地、東西沒有不見。這個抽屜叫 localStorage

短期記憶(狀態) 我要跟 = true 重整 → ✕ 消失 存進抽屜 localStorage(抽屜) 我要跟 = true 重整 → ✓ 還在 但只在這一台裝置、這個瀏覽器
圖 2.1localStorage 是瀏覽器裡的抽屜:放進去重整還在——但抽屜是裝置專屬的,換手機就是空的(第 6 堂的伏筆)。
重要限制(第 6 堂的伏筆):這個抽屜只在這一台裝置、這個瀏覽器裡。你今天放進去的東西,換一支手機打開同一個網頁,抽屜是空的;同事打開他的電腦,也看不到你的抽屜。
  • 用下面的 Prompt A 把「我要跟」的狀態存進 localStorage
  • 把人數點高一點——這次深呼吸,按下重新整理
  • 數字還在!你真正在「操作資料」了,不只是操作畫面

起手式 · 用自己的話改

A接上 localStorage
目前「我要跟」按鈕點擊後人數會變化,但重新整理頁面後又變回原本的數字。
請幫我把這個狀態存進瀏覽器的 localStorage,
這樣重新整理頁面後,之前的人數和「已加入」狀態都要維持。
B理解 localStorage 的局限
請解釋一下,如果我今天用手機打開這個午餐揪團頁,
剛剛在電腦上存進 localStorage 的「我要跟」紀錄,
手機上看得到嗎?為什麼?
有人按下重新整理、畫面資料仍然保留的驚喜瞬間氛圍照
圖 1.1重整之後狀態還在——資料第一次被「記住」了。

想親眼看抽屜裡面嗎?第 3 堂用過的「右鍵 → 檢查」在這裡也派得上用場:打開開發者工具,在上方分頁列找到 Application(中文介面叫「應用程式」),左邊清單點開 Local Storage、再點你的網址——剛剛存進抽屜的那筆資料就躺在裡面,左邊是它的名字、右邊是內容。

順手做個實驗:在那個畫面把這筆資料刪掉(在資料上按右鍵 → Delete),回到頁面重新整理——人數歸零了。這證明頁面顯示的數字真的是從抽屜讀出來的
02fetch:跟外部要資料

收拾完「留得住」,再往外走一步。畫面上顯示的天氣、匯率,通常不是寫死在程式碼裡的(它每天都在變)。你的頁面要現場發一個請求去跟氣象局的伺服器要資料——這個動作叫 fetch

🔁關鍵性質

fetch 是重複發生的動作——每次打開頁面、每次你要求它,它就會再要一次最新的資料。打開 Network 面板,你會看到頁面剛剛同時發出去了好幾條這樣的請求。

你的午餐揪團頁 ☀ 天氣 widget fetch 請求 回傳資料 26°C 氣象資料伺服器 公開、不用密碼 Network 面板 GET /weather 200 GET /logo.png 200 每次重整,這些請求都會重新發生一次——「請求是重複發生的動作」
圖 1.1fetch:頁面現場跟別的伺服器要資料,Network 面板看得到每一條請求。
03動手:天氣 widget

這一步指定用 Open-Meteoopen-meteo.com)當資料來源——公開、免註冊、不用金鑰。動手前,先把下面這個網址直接貼到瀏覽器的網址列,親眼看一次「原始資料」長什麼樣:

台北即時天氣(貼到網址列試試):
https://api.open-meteo.com/v1/forecast?latitude=25.03&longitude=121.56&current_weather=true
回來那串文字裡的 temperature 就是現在的氣溫——等一下你的頁面抓的,就是同一份資料。
  • 用下面的 Prompt C 加一個顯示台北天氣的小區塊(資料來源用 Open-Meteo,免金鑰)
  • 打開 Network 面板,重整頁面,找到剛剛那條天氣請求
  • 點開那條請求,看看回來的原始資料長什麼樣
  • 再開 Application → Local Storage:剛抓回來的天氣資料也存進抽屜了——「抓到 → 存進去 → 畫出來」完整走過一輪

起手式 · 用自己的話改

C加一個 fetch 外部資料的 widget
在頁面上加一個小區塊,顯示台北目前的天氣(溫度、天氣狀況)。
資料來源用 Open-Meteo 這個免金鑰的公開天氣 API,網址是:
https://api.open-meteo.com/v1/forecast?latitude=25.03&longitude=121.56&current_weather=true
每次抓到天氣資料時,同時把它存一份進 localStorage(連同抓取時間),
讓我可以在開發者工具的 Application 面板裡看到這份資料。
請告訴我這段程式碼發出的請求,我要怎麼在瀏覽器的 Network 面板裡找到它。
螢幕上顯示即時資料儀表、數據流動感的氛圍照
圖 3.1你的頁面顯示的不再是假資料,是這一刻真實世界正在發生的資料
04拉同一份:全班的共用清單

天氣是「別人準備好的資料」。接下來拉一份你們自己生出來的資料——還記得第 2 堂全班一起湊的共用地點清單嗎?它其實一直躺在課程網站上,就是一個公開的 json 檔,跟天氣一樣可以 fetch。

🔗抄 vs 拉

第 2 堂你是把清單內容進自己頁面——那是快照,同學後來加的地點你的頁面不會知道。今天改成 fetch:清單一更新,你的頁面重整就跟上。而且全班拉的是同一份

全班的清單就在這(貼到網址列試試):
https://vibe-course.mixxin.tw/submissions/lunch-locations.json
裡面的 locations 陣列,就是大家湊出來的每一家店。
  • 用下面的 Prompt D 接上共用清單:顯示「全班目前湊了幾家店」+隨機抽一家「不然今天吃這家?」
  • 跟旁邊的同學對答案:兩個人頁面上的總數一樣——因為拉的是同一份
  • 多重整幾次,看隨機推薦換來換去——每次重整都是重新 fetch、重新抽

起手式 · 用自己的話改

D接上全班共用的地點清單
我的頁面要接上全班共用的地點清單,清單網址:
https://vibe-course.mixxin.tw/submissions/lunch-locations.json
(回傳的 JSON 裡,locations 陣列就是所有地點,每一筆有 name、address、rating 等欄位)
請在頁面上加一個小區塊:
- 顯示「全班目前湊了 N 家店」(N 是清單的總數)
- 隨機抽一家,顯示「不然今天吃這家?」加上店名和評分
跟天氣一樣,抓到資料時存一份進 localStorage。
第 6 堂的伏筆:現在你對這份共用資料只能「讀」。想按「我要跟」把自己「寫」進去、讓同事的畫面上也看得到你?寫入是完全另一回事——要動到大家共用的東西,得先講信任邊界。那就是第 6 堂的主戲。
05點到為止:不是所有資料都能直接抓

像天氣這種「公開、不用密碼就能查」的資料,前端可以直接抓。但如果一個資料來源需要一組「通行密碼」(API key)才能用——這組密碼絕對不能寫在前端,因為前端的程式碼任何人打開瀏覽器都看得到。

公開資料 天氣、匯率、公開名單 前端可以直接抓 ✓ 需要「通行密碼」的資料 會員系統、金流、內部資料庫 密碼絕不能寫在前端 ✕
圖 2.1有些資料能直接拿、有些不行:需要 API key 的來源,密碼絕對不能出現在前端——第 6 堂會細講。
這件事第 6 堂會細講。今天只要記得:有些資料能直接拿,有些不行。

起手式 · 用自己的話改

E理解公開 vs 需金鑰
請解釋一下,像天氣這種資料為什麼不用密碼就能拿,
而有些資料來源需要密碼(API key)才能用?
那些密碼為什麼不能放在前端程式碼裡?
·作業與檢核

作業:一張 issue + 一個 PR——讓另一個資料也存進 localStorage,並加一個 fetch widget。PR 描述包含:「這個資料存在哪裡?重整、換裝置分別會不會不見?我加的 fetch 資料來源需不需要密碼?」

講師 / 助教檢核重點

  • 「我要跟」狀態在重整後仍保留(存進 localStorage)
  • 學員能正確回答「localStorage 換裝置看不看得到」
  • 每人至少成功加入一個 fetch 外部資料的 widget
  • 能在 Network 面板中指認出對應請求
交完之後 → 這堂的作業盤點出一個全班共通的盲點(14 份有 10 份中招),另開了一頁講清楚,並附一個特別指派作業:補課 · 抽屜的另一半 →