# 建立銀行應用程式第 3 部分:獲取和使用數據的方法 ## 課前測驗 [課前測驗](https://ff-quizzes.netlify.app/web/quiz/45) ### 簡介 在每個網絡應用程式的核心,都是*數據*。數據可以有多種形式,但其主要目的是向用戶顯示信息。隨著網絡應用程式變得越來越互動和複雜,用戶如何訪問和與信息互動已成為網絡開發中的關鍵部分。 在本課中,我們將學習如何從伺服器異步獲取數據,並使用這些數據在不重新加載 HTML 的情況下,在網頁上顯示信息。 ### 先決條件 在學習本課之前,你需要完成網絡應用程式的[登錄和註冊表單](../2-forms/README.md)部分。此外,你還需要安裝 [Node.js](https://nodejs.org) 並[本地運行伺服器 API](../api/README.md),以便獲取帳戶數據。 你可以通過在終端執行以下命令來測試伺服器是否正常運行: ```sh curl http://localhost:5000/api # -> should return "Bank API v1.0.0" as a result ``` --- ## AJAX 和數據獲取 傳統的網站在用戶選擇鏈接或使用表單提交數據時,會通過重新加載整個 HTML 頁面來更新顯示的內容。每次需要加載新數據時,網絡伺服器都會返回一個全新的 HTML 頁面,瀏覽器需要處理這些頁面,這會中斷當前的用戶操作並限制重新加載期間的互動。這種工作流程也被稱為*多頁應用程式*(Multi-Page Application,MPA)。  隨著網絡應用程式變得更加複雜和互動性更強,一種名為 [AJAX(Asynchronous JavaScript and XML)](https://en.wikipedia.org/wiki/Ajax_(programming)) 的新技術應運而生。這種技術允許網絡應用程式使用 JavaScript 從伺服器異步發送和檢索數據,而無需重新加載 HTML 頁面,從而實現更快的更新和更流暢的用戶互動。當從伺服器接收到新數據時,可以使用 [DOM](https://developer.mozilla.org/docs/Web/API/Document_Object_Model) API 通過 JavaScript 更新當前的 HTML 頁面。隨著時間的推移,這種方法演變成現在所謂的[*單頁應用程式*(Single-Page Application,SPA)](https://en.wikipedia.org/wiki/Single-page_application)。  在 AJAX 剛推出時,唯一可用的異步獲取數據的 API 是 [`XMLHttpRequest`](https://developer.mozilla.org/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest)。但現代瀏覽器現在還實現了更方便且功能更強大的 [`Fetch` API](https://developer.mozilla.org/docs/Web/API/Fetch_API),它使用 Promise,並且更適合處理 JSON 數據。 > 雖然所有現代瀏覽器都支持 `Fetch API`,但如果你希望你的網絡應用程式能在舊版或老舊瀏覽器上運行,最好先檢查 [caniuse.com 上的兼容性表](https://caniuse.com/fetch)。 ### 任務 在[上一課](../2-forms/README.md)中,我們實現了註冊表單來創建帳戶。現在,我們將添加代碼來使用現有帳戶登錄並獲取其數據。打開 `app.js` 文件,並添加一個新的 `login` 函數: ```js async function login() { const loginForm = document.getElementById('loginForm') const user = loginForm.user.value; } ``` 我們首先使用 `getElementById()` 獲取表單元素,然後通過 `loginForm.user.value` 獲取輸入框中的用戶名。每個表單控件都可以通過其名稱(在 HTML 中使用 `name` 屬性設置)作為表單的屬性來訪問。 與我們為註冊所做的類似,我們將創建另一個函數來執行伺服器請求,但這次是用於檢索帳戶數據: ```js 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 從伺服器異步請求數據,但這次我們不需要額外的參數,只需調用的 URL,因為我們只是查詢數據。默認情況下,`fetch` 創建一個 [`GET`](https://developer.mozilla.org/docs/Web/HTTP/Methods/GET) HTTP 請求,這正是我們需要的。 ✅ `encodeURIComponent()` 是一個用於對 URL 中的特殊字符進行編碼的函數。如果我們不調用此函數而直接在 URL 中使用 `user` 值,可能會出現什麼問題? 現在我們來更新 `login` 函數以使用 `getAccount`: ```js 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'); } ``` 首先,由於 `getAccount` 是一個異步函數,我們需要使用 `await` 關鍵字來等待伺服器結果。與任何伺服器請求一樣,我們還需要處理錯誤情況。目前,我們只添加一條日誌消息來顯示錯誤,稍後再處理。 然後,我們需要將數據存儲在某個地方,以便稍後用於顯示儀表板信息。由於 `account` 變量尚不存在,我們將在文件頂部創建一個全局變量: ```js let account = null; ``` 在用戶數據保存到變量後,我們可以使用已經存在的 `navigate()` 函數從*登錄*頁面導航到*儀表板*。 最後,我們需要在提交登錄表單時調用 `login` 函數,通過修改 HTML: ```html