# 构建银行应用程序第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(异步JavaScript和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