Get to know MDN better
此页面由社区从英文翻译而来。了解更多并加入 MDN Web Docs 社区。
在该教程中,我们将使用XMLHttpRequest 来发送 HTTP 请求以实现网站和服务器之间的数据交换。XMLHttpRequest常见和晦涩的使用情况都将包含在例子中。
发送一个 HTTP 请求,需要创建一个 XMLHttpRequest 对象,打开一个 URL,最后发送请求。当所有这些事务完成后,该对象将会包含一些诸如响应主体或 HTTP status 的有用信息。
通过 XMLHttpRequest 生成的请求可以有两种方式来获取数据,异步模式或同步模式。请求的类型是由这个 XMLHttpRequest 对象的 open() 方法的第三个参数async的值决定的。如果该参数的值为 false,则该 XMLHttpRequest请求以同步模式进行,否则该过程将以异步模式完成。这两种类型请求的详细讨论和指南可以在同步和异步请求页找到。
备注:由于对用户体验的负面影响,从 Gecko 30.0 版本开始,在主线程上的同步请求已经被弃用。
备注:XMLHttpRequest 构造函数并不仅限于 XML 文档。它之所以使用“XML”开头是因为在它诞生之时,原先用于异步数据交换的主要格式便是 XML。
W3C 规范定义了 XMLHttpRequest() 对象的几种类型的响应属性。这些属性告诉客户端关于 XMLHttpRequest 返回状态的重要信息。一些处理非文本返回类型的用例,可能包含下面章节所描述的一些操作和分析。
如果你使用 XMLHttpRequest 来获得一个远程的 XML 文档的内容,responseXML 属性将会是一个由 XML 文档解析而来的 DOM 对象,这很难被操作和分析。这里有五种主要的分析 XML 文档的方式:
备注:在 W3C XMLHttpRequest 规范中允许 HTML 通过 XMLHttpRequest.responseXML 属性进行解析。更多详细内容请阅读 HTML in XMLHttpRequest 。本条注意已在英文原文中更新。
备注:XMLHttpRequest 现在可以使用 responseXML 属性解释 HTML。请阅读 HTML in XMLHttpRequest 这篇文章了解相关用法。
如果使用 XMLHttpRequest 从远端获取一个 HTML 页面,则所有 HTML 标记会以字符串的形式存放在 responseText 属性里,这样就使得操作和解析这些标记变得困难。解析这些 HTML 标记主要有三种方式:
尽管 XMLHttpRequest 一般用来发送和接收文本数据,但其实也可以发送和接收二进制内容。有许多经过良好测试的方法来强制使用 XMLHttpRequest 发送二进制数据。利用 XMLHttpRequest 对象的 overrideMimeType() 方法是一个解决方案,虽然它并不是一个标准方法。
然而,自从 responseType 属性目前支持大量附加的内容类型后,已经出现了很多的现代技术,它们使得发送和接收二进制数据变得更加容易。
例如,考虑以下代码,它使用 "arraybuffer" 的 responseType 来将远程内容获取到一个存储原生二进制数据的 ArrayBuffer 对象中。
更多示例请参考 发送和接收二进制数据。
XMLHttpRequest 提供了各种在请求被处理期间发生的事件以供监听。这包括定期进度通知、错误通知,等等。
支持 DOM 的 progress 事件监测之于 XMLHttpRequest 传输,遵循 Web API 进度事件规范:这些事件实现了 ProgressEvent 接口。
progress检索的数据量发生了变化。
load传输完成,所有数据保存在 response 中。
第 3-6 行为多种事件添加了事件监听,这些事件在使用 XMLHttpRequest 执行数据传输时被发出。
备注:你需要在请求调用 open() 之前添加事件监听。否则 progress 事件将不会被触发。
在上一个例子中,progress 事件被指定由 updateProgress() 函数处理,并接收到传输的总字节数和已经传输的字节数,它们分别在事件对象的 total 和 loaded 属性里。但是如果 lengthComputable 属性的值是 false,那么意味着总字节数是未知并且 total 的值为零。
progress 事件同时存在于下载和上传的传输。下载相关事件在 XMLHttpRequest 对象上被触发,就像上面的例子一样。上传相关事件在 XMLHttpRequest.upload 对象上被触发,像下面这样:
备注:progress 事件在使用 file: 协议的情况下是无效的。
备注:从 Gecko 9.0 开始,进度事件现在可以依托于每一个传入的数据块,包括进度事件被触发前在已经接受了最后一个数据包且连接已经被关闭的情况下接收到的最后一个块。这种情况下,当该数据包的 load 事件发生时 progress 事件会被自动触发。这使你可以只关注 progress 事件就可以可靠的监测进度。
备注:在 Gecko 12.0 中,当 responseType 为 "moz-blob" 时,如果你的 progress 事件被触发,则响应的值是一个包含了接收到的数据的 Blob 。
使用 loadend 事件可以侦测到所有的三种加载结束条件(abort、load,或 error):
需要注意的是,没有方法可以确切的知道 loadend 事件接收到的信息是来自何种条件引起的操作终止;但是你可以在所有传输结束的时候使用这个事件处理。
XMLHttpRequest 的实例有两种方式提交表单:
第二种方式(使用 FormData API)是最简单最快捷的,但是缺点是被收集的数据无法使用 JSON.stringify() 转换为一个 JSON 字符串。 只使用 AJAX 则更为复杂,但也更灵活、更强大。
在大多数用例中,提交表单时即便不使用 FormData API 也不会要求其他的 API。唯一的例外情况是,如果你要上传一个或多个文件,你需要额外的 FileReader API。
一个 html <form> 可以用以下四种方式发送:
现在,我们提交一个表单,它里面有两个字段,分别被命名为 foo 和 baz。如果你用 POST 方法,那么服务器将会接收到一个字符串类似于下面三种情况之一,其中的区别依赖于你采用何种编码类型:
方法:POST;编码类型:text/plain:
Content-Type: text/plain foo=bar baz=The first line. The second line.方法:POST;编码类型:multipart/form-data:
Content-Type: multipart/form-data; boundary=---------------------------314911788813839 -----------------------------314911788813839 Content-Disposition: form-data; name="foo" bar -----------------------------314911788813839 Content-Disposition: form-data; name="baz" The first line. The second line. -----------------------------314911788813839--相反的,如果你用 GET 方法,像下面这样的字符串将被简单的附加到 URL:
?foo=bar&baz=The%20first%20line.%0AThe%20second%20line.所有这些事情都是由浏览器在你提交一个 <form> 的时候自动完成的。但是如果你想要用 JavaScript 做同样的事情,你不得不告诉解释器所有的事。那么,如何发送表单这件事在使用纯粹的 AJAX 时会复杂到无法在这里解释清楚。基于这个原因,我们提供一个完整的(但仍然教条的)框架,它可以使用所有的四种提交方式,甚至上传文件:
要测试它的话,创建一个名为 register.php 的页面(作为示例表单的 action 属性)并且只输入以下内容:
激活这些代码的语法很简单:
备注:该框架使用 FileReader API 进行文件的上传。这是一个较新的 API 并且还未在 IE9 及以下版本的浏览器中实现。因此,使用 AJAX 上传仍是一项实验性的技术。如果你不需要上传 二进制文件,该框架在大多数浏览器中运行良好。
备注:发送二进制内容的最佳途径是通过 ArrayBuffers 或 Blobs 结合 send() 方法甚至 FileReader API 的 readAsArrayBuffer() 方法。但是,自从该脚本的目的变成处理 可字符串化 的原始数据以来,我们使用 sendAsBinary() 方法结合 FileReader API 的 readAsBinaryString() 方法。同样地,上述脚本仅当你处理小文件时行之有效。如果不打算上传二进制内容,就考虑使用 FormData API 来替代。
备注:非标准的 sendAsBinary 方法从 Gecko 31 开始将会废弃并且会很快被移除。标准方法 send(Blob data) 将会取而代之。
FormData 构造函数能使你编译一个键/值对的集合,然后使用 XMLHttpRequest 发送出去。其主要用于发送表格数据,但是也能被单独用来传输表格中用户指定的数据。传输的数据格式与表格使用 submit() 方法发送数据的格式一致,如果该表格的编码类型被设为 "multipart/form-data"。FormData 对象可以被结合 XMLHttpRequest 的多种方法利用。例如,想了解如何利用 FormData 与 XMLHttpRequest,请转到使用 FormData 对象页面。为了说教的目的,这里有一个早期的示例,被转译成了使用 FormData API 的形式。注意以下代码片段:
备注:如之前所述,FormData 对象并不是 可字符串化 (stringifiable) 的对象。如果你想要字符串化一个提交数据,请使用这个 早期的纯 AJAX 例子. 同时也要注意,尽管这个例子中有一些 file <input> 字段,但当你通过 FormData API 提交一个表格时,也无须使用 FileReader API: 文件被自动加载并上传。
先创建两个函数:
And to test:
如果你想要了解当前页面是否发生了改变,请阅读这篇文章:document.lastModified。
现代浏览器通过实现跨源资源共享(CORS)标准来支持跨站请求。只要服务器端的配置允许你从你的 Web 应用发送请求,就可以使用 XMLHttpRequest。否则,会抛出一个 INVALID_ACCESS_ERR 异常
有一个跨浏览器兼容的方法,就是给 URL 添加时间戳。请确保你酌情地添加了 "?" or "&" 。例如,将:
http://example.com/bar.html -> http://example.com/bar.html?12345 http://example.com/bar.html?foobar=baz -> http://example.com/bar.html?foobar=baz&12345因为本地缓存都是以 URL 作为索引的,这样就可以使每个请求都是唯一的,也就可以这样来绕开缓存。
你也可以用下面的方法自动更改缓存:
要启用跨站脚本,推荐的做法是对 XMLHttpRequest 的响应使用 Access-Control-Allow-Origin 的 HTTP 标头。
如果你的 XMLHttpRequest 收到 status=0 和 statusText=null 的返回,这意味着请求无法执行。就是未被发送的(UNSENT)。一个可能导致的原因是在 XMLHttpRequest 在执行 open() 时,XMLHttpRequest 的来源发生了改变。这种情况是可能发生的,例如,我们在一个窗口的 onunload 事件触发时在进行一个 XMLHttpRequest,之前创建的 XMLHttpRequest 仍然在那里,最后当这个窗口失去焦点、另一个窗口获得焦点时,它还是发送了请求(也就是 open())。最有效的避免这个问题的方法是在关闭的窗口触发 unload 事件时为新窗口的 DOMActivate 事件设置一个监听器。
| XMLHttpRequest # interface-xmlhttprequest |
启用 JavaScript 以查看此浏览器兼容性表。