# 建立銀行應用程式第三部分:抓取及使用資料的方法 想想《星際爭霸戰》裡的企業號電腦——當皮卡爾艦長詢問艦況時,資訊立刻出現,整個介面不會關閉然後重建。這種無縫的資訊流正是我們現在用動態抓取資料要做的。 現在,你的銀行應用程式就像一份印刷報紙——資訊齊全但靜態。我們要把它轉變成更像 NASA 的任務控制中心,資料持續流動並即時更新,不會打斷使用者的工作流程。 你將學會如何非同步與伺服器溝通、處理不同時間抵達的資料,並將原始資訊轉換成對用戶有意義的內容。這也是 Demo 版本與可上線產品軟體的關鍵差別。 ## ⚡ 接下來 5 分鐘你可以做什麼 **忙碌開發者快速入門路徑** ```mermaid flowchart LR A[⚡ 5 分鐘] --> B[建立 API 伺服器] B --> C[使用 curl 測試抓取] C --> D[建立登入功能] D --> E[查看資料運作] ``` - **第1-2分鐘**:啟動你的 API 伺服器(`cd api && npm start`)並測試連線 - **第3分鐘**:使用 fetch 建立基本的 `getAccount()` 函式 - **第4分鐘**:用 `action="javascript:login()"` 連接登入表單 - **第5分鐘**:測試登入並在主控台觀察帳戶資料出現 **快速測試指令**: ```bash # 驗證 API 是否運行中 curl http://localhost:5000/api # 測試帳戶資料擷取 curl http://localhost:5000/api/accounts/test ``` **為什麼重要**:5 分鐘內,你將看到非同步資料抓取的魔力,它主導了所有現代網頁應用,讓應用感覺更即時、活躍。 ## 🗺️ 你的資料驅動式網頁應用學習旅程 ```mermaid journey title 從靜態頁面到動態應用程式 section 理解演變過程 傳統頁面重載: 3: You 發現 AJAX/SPA 的好處: 5: You 精通 Fetch API 模式: 7: You section 建立身份驗證 建立登入功能: 4: You 處理非同步操作: 6: You 管理使用者會話: 8: You section 動態 UI 更新 學習 DOM 操作: 5: You 建立交易展示: 7: You 創建響應式儀表板: 9: You section 專業模式 基於範本的渲染: 6: You 錯誤處理策略: 7: You 性能優化: 8: You ``` **旅程目的地**:完成本課後,你會理解現代網頁應用如何動態抓取、處理並顯示資料,創造出專業應用程式中無縫的使用體驗。 ## 課前小測驗 [課前小測驗](https://ff-quizzes.netlify.app/web/quiz/45) ### 先決條件 在開始資料抓取前,請確定以下元件已準備好: - **前一課程**:完成 [登入與註冊表單](../2-forms/README.md) — 我們會以此為基礎 - **本地伺服器**:安裝 [Node.js](https://nodejs.org) 並 [啟動 API 伺服器](../api/README.md) 以提供帳戶資料 - **API 連線**:用下列指令測試伺服器連線: ```bash curl http://localhost:5000/api # 預期回應: "Bank API v1.0.0" ``` 這個快速測試確保所有元件正常通訊: - 驗證 Node.js 在你的系統上正確運行 - 確認你的 API 伺服器正在回應 - 驗證應用程式能夠連上伺服器(就像任務前檢查無線電聯絡) ## 🧠 資料管理生態系統概覽 ```mermaid mindmap root((資料管理)) Authentication Flow Login Process 表單驗證 憑證驗證 會話管理 User State 全域帳戶物件 導航守衛 錯誤處理 API Communication Fetch Patterns GET 請求 POST 請求 錯誤回應 Data Formats JSON 處理 URL 編碼 回應解析 Dynamic UI Updates DOM Manipulation 安全文字更新 元素創建 範本複製 User Experience 即時更新 錯誤訊息 載入狀態 Security Considerations XSS Prevention textContent 使用 輸入消毒 安全 HTML 創建 CORS Handling 跨來源請求 標頭設定 開發環境設定 ``` **核心原則**:現代網頁應用是資料協調系統——它們在用戶介面、伺服器 API 與瀏覽器安全模型之間協同運作,創造無縫且即時反應的體驗。 --- ## 理解現代網頁應用的資料抓取 過去二十年來,網頁應用處理資料的方式發生了劇烈改變。了解這個演進,有助於你體會為何 AJAX 與 Fetch API 這類現代技術如此強大,並成為網頁開發者必備。 讓我們來探索傳統網站如何運作、以及我們今天建立的動態、反應快速的應用的差異。 ### 傳統多頁應用(MPA) 在網路初期,每一次點擊就像老電視切換頻道——螢幕會變空白,然後慢慢調到新內容。這就是早期網頁應用的現實:每一個互動都需完整重建整頁。 ```mermaid sequenceDiagram participant User participant Browser participant Server User->>Browser: 點擊連結或提交表單 Browser->>Server: 請求新的 HTML 頁面 Note over Browser: 頁面變空白 Server->>Browser: 回傳完整的 HTML 頁面 Browser->>User: 顯示新頁面(閃爍/重新載入) ``` ![多頁應用的更新流程](../../../../translated_images/zh-TW/mpa.7f7375a1a2d4aa77.webp) **這種方式的笨重之處:** - 每次點擊都要重新載入整頁 - 使用者思考中會被頁面閃爍打斷 - 網路連線要不停重複下載相同頁首與頁尾 - 應用像是在翻閱紙本檔案櫃,而不是用軟體 ### 現代單頁應用(SPA) AJAX(非同步 JavaScript & XML)徹底改變了這個模式。就像國際太空站模組化設計,太空人可以更換個別元件而無需重建整個結構,AJAX 讓我們能更新網頁特定部分而非重新載入全部。雖然名稱提到 XML,但現在大多用 JSON,核心原則是一樣的:只更新需要變動的部分。 ```mermaid sequenceDiagram participant User participant Browser participant JavaScript participant Server User->>Browser: 與頁面互動 Browser->>JavaScript: 觸發事件處理器 JavaScript->>Server: 只取得所需的資料 Server->>JavaScript: 回傳 JSON 資料 JavaScript->>Browser: 更新特定的頁面元素 Browser->>User: 顯示更新內容(無重新載入) ``` ![單頁應用的更新流程](../../../../translated_images/zh-TW/spa.268ec73b41f992c2.webp) **SPA 讓使用體驗更好的原因:** - 只更新真的改變的部分(很聰明!) - 不再有刺眼閃爍,使用者保持流程不中斷 - 傳輸資料較少,載入更快速 - 整體感覺更流暢反應快,就像手機應用程式 ### 進化到現代 Fetch API 現代瀏覽器提供了 [`Fetch` API](https://developer.mozilla.org/docs/Web/API/Fetch_API),取代了舊式的 [`XMLHttpRequest`](https://developer.mozilla.org/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest/)。就像電報到電子郵件的改變,Fetch API 利用 Promise 寫出更乾淨的非同步程式碼,且自然處理 JSON。 | 功能 | XMLHttpRequest | Fetch API | |---------|----------------|----------| | **語法** | 複雜回呼函式 | 乾淨的 Promise | | **JSON 處理** | 需手動解析 | 內建 `.json()` 方法 | | **錯誤處理** | 資訊有限 | 全面錯誤細節 | | **現代支援** | 傳統相容 | ES6+ 的 Promise 与 async/await | > 💡 **瀏覽器相容性**:好消息是 Fetch API 支援所有現代瀏覽器!若想知道版本詳情,[caniuse.com](https://caniuse.com/fetch) 提供完整資訊。 > **結論:** - Chrome、Firefox、Safari、Edge (也就是大多數使用者)支援完善 - 只有 Internet Explorer 需額外處理(是時候放手 IE 了) - 為我們後面使用的優雅 async/await 風格做好鋪墊 ### 實作用戶登入與資料抓取 現在來實作登入系統,將你的銀行應用從靜態顯示變成具功能的應用。像安全軍事設施的驗證協定一樣,我們會驗證使用者身分,再給予其專屬資料的存取權。 我們將逐步建構,先做基本身份驗證,再加入抓取資料的功能。 #### 第一步:建立登入函式基礎 打開你的 `app.js` 並新增 `login` 函式,處理用戶認證流程: ```javascript async function login() { const loginForm = document.getElementById('loginForm'); const user = loginForm.user.value; } ``` **解析此段程式碼:** - `async` 關鍵字?它告訴 JavaScript 「嘿,這函式可能要等一些非同步事情完成」 - 我們從頁面抓到表單(沒什麼特別,直接用 ID 找) - 接著取出用戶輸入的使用者名稱 - 這裡有個小妙招:你可以用表單欄位的 `name` 屬性存取輸入框,省去 getElementById 步驟! > 💡 **表單存取模式**:每個表單控制項都可以用它在 HTML 裡的 `name` 屬性當成表單元素的屬性來存取,讓獲取資料更簡潔易讀。 #### 第二步:建立帳戶資料抓取函式 接著,我們要寫一個專門從伺服器取得帳戶資料的函式。它跟你之前註冊使用的函式結構類似,但專注於資料取得: ```javascript async function getAccount(user) { try { const response = await fetch('//localhost:5000/api/accounts/' + encodeURIComponent(user)); return await response.json(); } catch (error) { return { error: error.message || 'Unknown error' }; } } ``` **這段程式碼的目的:** - **運用** 現代 `fetch` API 非同步抓取資料 - **構造** 搭配使用者名稱的 GET 請求 URL - **使用** `encodeURIComponent()` 來安全處理 URL 中的特殊字元 - **轉換** 回應成 JSON 格式方便資料操作 - **優雅** 處理錯誤,回傳錯誤物件而非當機 > ⚠️ **安全提醒**:`encodeURIComponent()` 處理 URL 裡的特殊字元,像海軍通訊中的編碼系統,保證訊息精確送達,避免「#」或「&」等字元誤解。 > **為何這很重要:** - 防止特殊字元破壞 URL - 防止 URL 操控攻擊 - 確保伺服器收到正確資料 - 遵守安全編碼原則 #### 了解 HTTP GET 請求 你可能會驚訝:用 `fetch` 預設不傳參數時,它自動是個 [`GET`](https://developer.mozilla.org/docs/Web/HTTP/Methods/GET) 請求。這正好適合我們用來詢問伺服器:「嗨,我能看到這用戶的帳戶資料嗎?」 GET 請求就像禮貌問借書——你是要看到已存在的東西。POST 請求(我們註冊用的)則像是要新增新書。 | GET 請求 | POST 請求 | |-------------|-------------| | **用途** | 取回現有資料 | 傳送新資料給伺服器 | | **參數** | 在 URL 路徑/查詢字串 | 包在請求主體 | | **快取** | 可被瀏覽器快取 | 通常不快取 | | **安全** | 在 URL/日誌可見 | 藏在請求主體裡 | ```mermaid sequenceDiagram participant B as 瀏覽器 participant S as 伺服器 Note over B,S: GET 請求(資料檢索) B->>S: GET /api/accounts/test S-->>B: 200 OK + 帳戶資料 Note over B,S: POST 請求(資料提交) B->>S: POST /api/accounts + 新增帳戶資料 S-->>B: 201 已建立 + 確認訊息 Note over B,S: 錯誤處理 B->>S: GET /api/accounts/nonexistent S-->>B: 404 找不到 + 錯誤訊息 ``` #### 第三步:整合所有功能 現在最令人滿意的部分——將帳戶抓取函式連接到登入流程。讓一切串聯起來: ```javascript async function login() { const loginForm = document.getElementById('loginForm'); const user = loginForm.user.value; const data = await getAccount(user); if (data.error) { return console.log('loginError', data.error); } account = data; navigate('/dashboard'); } ``` 這個函式按明確流程進行: - 從表單輸入取出使用者名稱 - 向伺服器請求該用戶帳戶資料 - 處理流程中發生的任何錯誤 - 成功後記錄帳戶資料並進入儀表板頁面 > 🎯 **Async/Await 模式**:因為 `getAccount` 是非同步函式,我們用關鍵字 `await` 等待伺服器回應,避免程式執行到一半資料還沒來。 #### 第四步:為你的資料建立儲存處 應用程式需要一個地方儲存載入的帳戶資訊。就像應用的短期記憶,讓目前使用者資料隨時可得。在 `app.js` 檔案頂端加入這行: ```javascript // 這裡保存目前使用者的帳戶資料 let account = null; ``` **我們為什麼需要它:** - 讓帳戶資料可從應用任何地方取得 - 從 `null` 開始表示「還沒有人登入」狀態 - 當有人成功登入或註冊,資料會更新 - 充當唯一資料真實來源,避免登入狀態混亂 #### 第五步:連接你的表單 現在讓新寫好的登入函式連結到 HTML 表單。更新你的 form 標籤如下: ```html
``` **這樣改變了什麼:** - 阻止表單預設「重新載入整頁」的行為 - 改成呼叫自訂 JavaScript 函式 - 保持流程順暢像單頁應用程式 - 你將完全掌控使用者按「登入」後會發生的事 #### 第六步:強化你的註冊函式 為了保持一致,更新 `register` 函式,也將帳戶資料存起來並進入儀表板: ```javascript // 將這些程式碼行加入您的註冊函式結尾 account = result; navigate('/dashboard'); ``` **此改善帶來:** - 註冊後能無縫切換到儀表板 - 登入與註冊流程的一致使用者體驗 - 成功註冊後立刻獲得帳戶資訊 #### 測試你的實作 ```mermaid flowchart TD A[使用者輸入帳號密碼] --> B[呼叫登入功能] B --> C[從伺服器抓取帳戶資料] C --> D{資料是否成功接收?} D -->|Yes| E[全域儲存帳戶資料] D -->|No| F[顯示錯誤訊息] E --> G[導向儀表板] F --> H[使用者停留在登入頁面] ``` **接下來來試試看:** 1. 新建立一個帳號確保流程通暢 2. 用同帳號嘗試登入 3. 若不正常,打開瀏覽器主控台(F12)查看錯誤訊息 4. 成功登入後應該進到儀表板 若出了問題,別慌!大多是拼字錯誤或忘記啟動 API 伺服器。 #### 關於跨來源(CORS)的簡短說明 你可能會好奇:「我的網頁應用跟 API 伺服器在不同 port 上,怎麼能溝通?」好問題!這是每位前端開發者都會遇到的課題。 > 🔒 **跨來源安全**:瀏覽器執行「同源政策」防止不同來源間未經授權的溝通,就像五角大廈的嚴格管控點,確認通訊被允許才進行資料傳送。 > **在我們的設定中:** - 網頁應用在 `localhost:3000`(開發伺服器) - API 伺服器在 `localhost:5000`(後端伺服器) - API 伺服器設定了 [CORS 標頭](https://developer.mozilla.org/docs/Web/HTTP/CORS),明確允許你的網頁應用溝通 這樣的配置反映真實開發環境,前後端通常分開在不同伺服器上執行。 > 📚 **深入了解**:透過這個全面的 [Microsoft Learn API 課程](https://docs.microsoft.com/learn/modules/use-apis-discover-museum-art/?WT.mc_id=academic-77807-sagibbon) 探索更多 API 與資料抓取知識。 ## 讓你的資料在 HTML 裡「活」起來 現在我們要透過 DOM 操控,讓抓取的資料展現給使用者看。就像在暗房裡洗照片,我們將隱藏的資料轉成使用者能看到、互動的內容。 DOM 操作是將靜態網頁轉變為根據使用者互動和伺服器回應更新內容的動態應用程式的技術。 ### 選擇合適的工具 當需要用 JavaScript 更新你的 HTML 時,你有幾個選擇。可以把它們想像成工具箱裡的不同工具——每個都適合特定的工作: | 方法 | 適合用途 | 適用時機 | 安全等級 | |--------|---------------------|----------------|--------------| | `textContent` | 安全顯示使用者資料 | 任何時候要顯示文字 | ✅ 非常可靠 | | `createElement()` + `append()` | 建立複雜版面配置 | 建立新區塊/列表 | ✅ 非常安全 | | `innerHTML` | 設定 HTML 內容 | ⚠️ 盡量避開使用 | ❌ 有風險 | #### 安全顯示文字的方式:textContent [`textContent`](https://developer.mozilla.org/docs/Web/API/Node/textContent) 屬性是顯示使用者資料時的好幫手。它就像你網頁的保鑣——任何有害的東西都不會通過: ```javascript // 安全且可靠的文字更新方式 const balanceElement = document.getElementById('balance'); balanceElement.textContent = account.balance; ``` **textContent 的好處:** - 當作純文字處理(防止腳本執行) - 自動清除現有內容 - 對簡單文字更新高效 - 提供內建的惡意內容防護 #### 建立動態 HTML 元素 對於更複雜的內容,將 [`document.createElement()`](https://developer.mozilla.org/docs/Web/API/Document/createElement) 與 [`append()`](https://developer.mozilla.org/docs/Web/API/ParentNode/append) 方法結合使用: ```javascript // 建立新元素的安全方式 const transactionItem = document.createElement('div'); transactionItem.className = 'transaction-item'; transactionItem.textContent = `${transaction.date}: ${transaction.description}`; container.append(transactionItem); ``` **這種方法的理解:** - **程式化建立**新的 DOM 元素 - **完全控制**元素屬性與內容 - **允許**建立複雜且巢狀的元素結構 - **保持**結構與內容分離以維護安全 > ⚠️ **安全考量**:雖然 [`innerHTML`](https://developer.mozilla.org/docs/Web/API/Element/innerHTML) 在許多教學中出現,但它會執行嵌入的腳本。就像 CERN 的安全協議防止未授權代碼執行一樣,使用 `textContent` 和 `createElement` 是更安全的替代方案。 > **innerHTML 的風險:** - 會執行使用者資料中的 `