客户端网页API(二)——从服务器获取数据

在现代网站和应用中另一个常见的任务是从服务端获取个别数据来更新部分网页而不用加载整个页面。 这看起来是小细节却对网站性能和行为产生巨大的影响。所以我们将在这篇文章介绍概念和技术使它成为可能,例如: XMLHttpRequest 和 Fetch API.


这里有什么问题?

最初加载页面很简单 – 你为网站发送一个请求到服务器, 只要没有出错你将会获取资源并显示网页到你的电脑上。

这个模型的问题是当你想更新网页的任何部分,例如显示一套新的产品或者加载一个新的页面,你需要再一次加载整个页面。这是非常浪费的并且导致了差的用户体验尤其是现在的页面越来越大且越来越复杂。


Ajax简介

这导致了创建允许网页请求小块数据(例如 HTML, XML, JSON, 或纯文本) 和 仅在需要时显示它们的技术,从而帮助解决上述问题。

这是通过使用诸如 XMLHttpRequest 之类的API或者 — 最近以来的 Fetch API 来实现. 这些技术允许网页直接处理对服务器上可用的特定资源的 HTTP 请求,并在显示之前根据需要对结果数据进行格式化。

注意在早期,这种通用技术被称为Asynchronous JavaScript and XML(Ajax), 因为它倾向于使用XMLHttpRequest 来请求XML数据。 但通常不是这种情况 (你更有可能使用 XMLHttpRequest 或 Fetch 来请求JSON), 但结果仍然是一样的,术语“Ajax”仍然常用于描述这种技术。


Ajax模型包括使用Web API作为代理来更智能地请求数据,而不仅仅是让浏览器重新加载整个页面。让我们来思考这个意义:

  1. 去你最喜欢的信息丰富的网站之一,如亚马逊,油管,CNN等,并加载它。
  2. 现在搜索一些东西,比如一个新产品。 主要内容将会改变,但大部分周围的信息,如页眉,页脚,导航菜单等都将保持不变。

这是一件非常好的事情,因为:

  • 页面更新速度更快,您不必等待页面刷新,这意味着该网站体验感觉更快,响应更快。
  • 每次更新都会下载更少的数据,这意味着更少地浪费带宽。在宽带连接的桌面上这可能不是一个大问题,但是在移动设备和发展中国家没有无处不在的快速互联网服务是一个大问题。

为了进一步提高速度,有些网站还会在首次请求时将资产和数据存储在用户的计算机上,这意味着在后续访问中,他们将使用本地版本,而不是在首次加载页面时下载新副本。 内容仅在更新后从服务器重新加载。

本文不会涉及这种存储技术。我们稍后会在模块中讨论它。


基本的Ajax请求

让我们看看使用 XMLHttpRequestFetch 如何处理这样的请求. 对于这些例子,我们将从几个不同的文本文件中请求数据,并使用它们来填充内容区域。

这一系列文件将作为我们的假数据库; 在真正的应用程序中,我们更可能使用服务器端语言(如PHP,Python或Node)从数据库请求我们的数据。 不过,我们要保持简单,并专注于客户端部分。

XMLHttpRequest

XMLHttpRequest (通常缩写为XHR)现在是一个相当古老的技术 - 它是在20世纪90年代后期由微软发明的,并且已经在相当长的时间内跨浏览器进行了标准化。

  1. 为例子做些准备, 将 ajax-start.html 和四个文本文件 — verse1.txt, verse2.txt, verse3.txt, verse4.txt — 复制到你计算机上的一个新目录. 在这个例子中,我们将通过XHR在下拉菜单中选择一首诗(您可能会认识 — “如果谷歌翻译可以翻译的话”)加载另一首诗。

  2. <script> 的内部, 添加下面的代码. 将 <select><pre> 元素的引用存储到变量中, 并定义一个 onchange 事件处理函数,可以在 select 的值改变时, 将其值传递给 updateDisplay() 函数作为参数。

const verseChoose = document.querySelector('select');
const poemDisplay = document.querySelector('pre');

verseChoose.onchange = function() {
  const verse = verseChoose.value;
  updateDisplay(verse);
};
  1. 定义 updateDisplay() 函数. 首先,将下面的代码块放在之前代码块的下面 - 这是函数的空壳:
function updateDisplay(verse) {

}
  1. 我们将通过构造一个 指向我们要加载的文本文件的相对URL 来启动我们的函数, 因为我们稍后需要它. 任何时候 <select> 元素的值都与所选的 <option> 内的文本相同 (除非在值属性中指定了不同的值) — 例如 “Verse 1”. 相应的诗歌文本文件是 “verse1.txt”, 并与HTML文件位于同一目录中, 因此只需要文件名即可。
    但是,Web服务器往往是区分大小写的,文件名没有空格。 要将“Verse 1”转换为“verse1.txt”,我们需要将V转换为小写,删除空格,并在末尾添加.txt。 这可以通过 replace(), toLowerCase(), 和 简单的 string concatenation 来完成. 在 updateDisplay() 函数中添加以下代码:
verse = verse.replace(" ", "");
verse = verse.toLowerCase();
let url = verse + '.txt';
  1. 要开始创建XHR请求,您需要使用 XMLHttpRequest() 的构造函数创建一个新的请求对象。 你可以把这个对象叫做你喜欢的任何东西, 但是我们会把它叫做 request 来保持简单. 在之前的代码中添加以下内容:
let request = new XMLHttpRequest();
  1. 接下来,您需要使用 open() 方法来指定用于从网络请求资源的 HTTP request method , 以及它的URL是什么。我们将在这里使用 GET 方法, 并将URL设置为我们的 url 变量. 在你上面的代码中添加以下代码:
request.open('GET', url);
  1. 接下来,我们将设置我们期待的响应类型 — 这是由请求的 responseType 属性定义的 — 作为 text. 这并不是绝对必要的 — XHR默认返回文本 —但如果你想在以后获取其他类型的数据,养成这样的习惯是一个好习惯. 接下来添加:
request.responseType = 'text';
  1. 从网络获取资源是一个 asynchronous “异步” 操作, 这意味着您必须等待该操作完成(例如,资源从网络返回),然后才能对该响应执行任何操作,否则会出错,将被抛出错误。 XHR允许你使用它的 onload 事件处理器来处理这个事件 — 当 onload 事件触发时(当响应已经返回时)这个事件会被运行。 发生这种情况时, response 数据将在XHR请求对象的响应属性中可用。
    在后面添加以下内容. 你会看到,在 onload 事件处理程序中,我们将 poemDisplay ( <pre> 元素 ) 的 textContent 设置为 request.response 属性的值。
request.onload = function() {
  poemDisplay.textContent = request.response;
};
  1. 以上都是XHR请求的设置 — 在我们告诉它之前,它不会真正运行,这是通过 send() 完成的. 在你之前的代码下添加以下内容完成该函数:
request.send();
  1. 这个例子中的一个问题就是它首次加载时不会显示任何诗。 为了解决这个问题,在代码的底部添加以下两行 (正好在关闭的 </script> 标签之上) 默认加载第1节,并确保 <select> 元素始终显示正确的值:
updateDisplay('Verse 1');
verseChoose.value = 'Verse 1';

Fetch

Fetch API基本上是XHR的一个现代替代品——它是最近在浏览器中引入的,它使异步HTTP请求在JavaScript中更容易实现,对于开发人员和在Fetch之上构建的其他API来说都是如此。

让我们将最后一个示例转换为使用Fetch !

  1. 复制您之前完成的示例目录.
  2. updateDisplay() 里找到 XHR 那段代码:
let request = new XMLHttpRequest();
request.open('GET', url);
request.responseType = 'text';

request.onload = function() {
  poemDisplay.textContent = request.response;
};

request.send();
  1. 像这样替换掉所有关于XHR的代码,运行应该与XHR版本相同
fetch(url).then(function(response) {
  response.text().then(function(text) {
    poemDisplay.textContent = text;
  });
});

那么Fetch代码中发生了什么呢?

首先,我们调用了 fetch() 方法,将我们要获取的资源的URL传递给它。这相当于现代版的XHR中的request.open(),另外,您不需要任何等效的 send() 方法。

然后,你可以看到.then() 方法连接到了 fetch() 末尾-这个方法是 Promises 的一部分,是一个用于执行异步操作的现代JavaScript特性。fetch() 返回一个 promise,它将解析从服务器发回的响应。我们使用 then() 来运行一些后续代码,这是我们在其内部定义的函数。这相当于XHR版本中的 onload 事件处理程序。

fetch() promise 解析时,这个函数会自动将响应从服务器传递给参数。在函数内部,我们获取响应并运行其text() 方法。这基本上将响应作为原始文本返回,这相当于在XHR版本中的 responseType = 'text'

你会看到 text() 也返回了一个 promise, 所以我们连接另外一个 .then() 到它上面, 在其中我们定义了一个函数来接收 text() promise 解析的生文本。

promise 的函数内部,我们做的和在XHR版本中差不多— 设置 <pre> 元素的文本内容为 text 的值。


案例——虚构超市

为了使本文更详尽,我们将看一个稍微复杂一点的示例,它展示了Fetch的一些更有趣的用法。我们创建了一个名为Can Store的示例站点——它是一个虚构的超市,只出售罐头食品。你可以找到 example live on GitHub,并且 see the source code

默认情况下,站点会显示所有产品,但您可以使用左手列中的表单控件按类别或搜索词或两者进行筛选。

有很多复杂的代码处理按类别和搜索词过滤产品、操作字符串以便数据在UI中正确显示等等。我们不会在本文中讨论所有这些,但是您可以在代码中找到大量的注释 (see can-script.js)。

但是,我们会解释Fetch代码的含义。

第一个使用Fetch的块可以在JavaScript的开头找到:

fetch('products.json').then(function(response) {
  if(response.ok) {
    response.json().then(function(json) {
      products = json;
      initialize();
    });
  } else {
    console.log('Network request for products.json failed with response ' + response.status + ': ' + response.statusText);
  }
});

这看起来和我们之前看到的相似,只是第二个 promise 是在一个条件语句中。在条件下,我们检查返回的response 是否成功 — response.ok 属性包含一个布尔变量,如果 response 是成功的 (e.g. 200 meaning “OK”),则是 true;如果失败了,则是false。

如果 response 成功,我们运行第二个 promise — 然而,这次我们使用 json() ,而不是 text() , 因为我们想要response 返回的是一个结构化的JSON数据,而不是纯文本。

如果 response 失败,我们将输出一个错误到控制台,指出网络请求失败,该控制台将报告响应的网络状态和描述性消息(分别包含在 response.statusresponse.statusText 属性中)。 当然,一个完整的web站点可以通过在用户的屏幕上显示一条消息来更优雅地处理这个错误,也许还可以提供一些选项来补救这种情况。

第二个Fetch块可以在 fetchBlob() 找到:

fetch(url).then(function(response) {
  if(response.ok) {
    response.blob().then(function(blob) {
      objectURL = URL.createObjectURL(blob);
      showProduct(objectURL, product);
    });
  } else {
    console.log('Network request for "' + product.name + '" image failed with response ' + response.status + ': ' + response.statusText);
  }
});

它的工作原理和前一个差不多, 除了我们放弃 json() 而使用 blob() — 在本例中,我们希望以图像文件的形式返回响应,为此使用的数据格式是Blob — 这个词是“二进制大对象”的缩写,基本上可以用来表示大型文件类对象——比如图像或视频文件。

一旦我们成功地接收到我们的 blob,我们就会使用它创建一个对象URL createObjectURL(). 它返回一个指向浏览器中引用的对象的临时内部URL。这些不是很容易读懂,但是你可以通过打开Can Store看到,按Ctrl-/右键单击一个图像,然后选择“View Image(查看图像)”选项(根据您使用的浏览器可能略有不同)。 对象URL将在地址栏中可见,应该是这样的:

blob:http://127.0.0.1:8080/b1d26d95-a076-4eb8-929c-0b38e61064c6
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值