AJAX & XMLHttpRequest

Posted Turbo的札记

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了AJAX & XMLHttpRequest相关的知识,希望对你有一定的参考价值。

建议使用非手机端浏览本文。

全名Asynchronous javascript and XML。
一般把他称为AJAX技术或AJAX通信,正常的客户端服务端数据交互,要么就是通过URL载入网页,或者是网页表单,但这两种方式都需要靠刷新/从头加载页面完成;
而AJAX这种技术,是通过JavaScript的异步通信,从服务器获取数据,再通过脚本部分更新页面,不用刷新整个页面。简言之就是
通过脚本发起的HTTP通信 (本质),然后拿到响应数据取更新页面。
也就是说只要通过脚本发起的通信,就可以叫做AJAX通信。

至于全名中的XML,指的是脚本发起通信后,服务器返回的数据是储存在XML文档中,但这已经是历史了,现在早已弃用XML,服务器传来的数据一般是JSON格式,但这种叫法一直沿用下来。

优势:
网速慢时,应用AJAX技术只是更新部分内容,用户可以浏览其余内容;
传统方法提交表单后若内容不合格页面会被刷新,要重新填写所有内容;
传统页面更新页面内容要重新载入页面,然而有些内容是重复的,会造成资源浪费、增加用户等待时间......
应用示例:
新闻应用信息流中下拉加载更多新闻;
列表数据无刷新分页;
表单项离开焦点后对数据进行不刷新页面的实时验证;
搜索框根据已输入的关键字提供联想补全备选项......(本质都是在不刷新页面的情况下部分更新页面)

在理解这项技术时,不要受这个AJAX这个名字的限制,不要过分关注异步这个字眼,也不要被上面的示例限制你的思维。
把他单纯的理解为通过JavaScript发起HTTP通信的方式即可,事实也是如此,下面全文基本是围绕XMLHttpRequest这个JavaScript的原生类展开的,关注的重点在于JS提供了一个实现HTTP通信的原生类,不关AJAX的事儿。

1.基本原理
AJAX通信包括一下几个步骤:
  1. 创建XMLHttpRequest实例
  2. 发出HTTP请求
  3. 接受服务器传回数据
  4. 更新网页数据
XMLHttpRequest是JavaScript的原生API,整个过程围绕该实例展开,这个原生对象提供了很多方法属性,能够自己发送HTTP请求并接收处理服务器的响应消息,所以并不需要自己写HTTP报文,至要把相关参数填入该实例的方法即可(这需要你对HTTP协议有一定的了解)。
XMLHttpRequest 不仅可以发送HTTP请求,还可以发送文件,这就是 AJAX 文件上传。
XMLHttpRequest这个对象是HTTP协议规范在客户端JS中的实现。
和前文说的一个道理,关键字里这个XML也是历史遗留的叫法。
XMLHttpRequest 本身是一个构造函数,使用new命令生成一个实例,整个AJAX通信过程均建立在这个示例上。

2.XMLHttpRequest的实例方法
实现JavaScript内发起HTTP通信的主体部分,除此之外还有两个方法用于访问响应报文的报头。
2.1 XMLHttpRequest.open()
指定HTTP请求参数,初始化实例对象,与服务器建立连接,共接受五个参数。
  
    
    
  
var xhr = new XMLHttpRequest(); xhr.open(    string method,          //HTTP方法    string url, //发送请求的目标URL    optional boolean async, //表示是否异步,默认值true,如果是false的话,只有与服务器连接建立后收到服务器响应后,才会进行后面的的代码,同步AJAX请求会造成服务器失去响应。许多浏览器已经禁止在主线程使用,只允许Worker里面使用。所以,这个参数轻易不应该设为false。 ‍‍ optional string user, //用于认证的永明,默认为空字符串。 optional string password//用于认证的密码,默认空字符串 );
对同一个AJAX请求(XMLHttpRequest实例)再次调用open方法,等同于调用abort()方法(详见后),即终止请求。
2.2XMLHttpRequest.setRequestHeader()
  • 用于设置HTTP请求得头信息。该方法得调用必须在open之后,send之前。
  • 方法接受两个参数,第一个是字符串,写字段名,第二个写字段值。
  • 如果重复设置同一个字段,每一次调用的值会被合并成一个单一的值发送。
  • 该方法可选,也就是说可以不设置头信息,没有设置得头信息字段会取默认值。
2.3XMLHttpRequest.overrideMimeType()    (详见后)
  • 用来指定 MIME 类型,覆盖服务器返回的真正的 MIME 类型,从而让浏览器进行不一样的处理。举例来说,服务器返回的数据类型是text/xml,由于种种原因浏览器解析不成功报错,这时就拿不到数据了。为了拿到原始数据,我们可以把 MIME 类型改成text/plain,这样浏览器就不会去自动解析,从而可以拿到原始文本。
  • 接受一个字符串类型参数,同样要在open后send前调用,该方法亦是可选。
  • 修改服务器返回的数据类型,不是正常情况下应该采取的方法。如果希望服务器返回指定的数据类型,可以用responseType属性告诉服务器(详见后)。只有在服务器无法返回某种数据类型时,才使用该方法。
2.4 XMLHttpRequest.send()
  • send方法用于发出HTTP请求以及获取请求数据,该方法的参数即请求报文中的请求数据。
  • 和请求报文的请求数据一样,数据可以是各种格式,可以是文件,这就是前文提到的AJAX上传文件。 //不过传文件要用到formData,这里就不岔开话题了。
  • 在open同服务器建立连接后,用send发出实际的HTTP请求,他的参数是可选的,但没有参数就要输入null,如果不带参数就表示HTTP请求只有一个URL,没有数据体,例如GET请求,反之例如POST请求。
  • open、send两个必须方法依次调用,共同构成了上文提到的发送HTTP请求这一步。
  • 虽然没有写HTTP报文,其实调用这几个方法的过程也算是写了:open→请求行,setRequestHeader→请求头,send→请求数据。
2.5 XMLHttpRequest.abort()
  • 用来终止已经发出的HTTP请求,该方法没有参数,对实例调用后,会终止实例对应的HTTP请求。
  • 调用这个方法以后,readyState属性变为4,status属性变为0(详见后)。
  • 说是停止HTTP请求,但实际上整个HTTP通信、整个AJAX过程都被停掉了。调用这个方法后,除了上面两条,还发生了这些事:对应的XHR实例会停止触发事件、不再允许访问任何与响应有关的对象属性(详见后)、终止请求之后还会对XHR对象进行解引用操作。可以这样说,abort()方法调用后,这个XHR对象已经不能用了。
  • 由上面三条及二次调用open()方法的效果可知,在同一XMLHttpRequest对象的实例上连续发起两次HTTP/AJAX通信是行不通的,语法不支持这样,请再new一个新的实例。
上面五个都是与发送HTTP请求相关的方法,接下来两个是与响应消息有关的两个方法。
2.6 XMLHttpRequest.getResponseHeader()
  • 参数为一个字符串,内容为响应消息的头信息字段。用来获取响应报文头信息中指定字段的值。
  • 如果未收到服务器回应或指定字段不存在,返回null。
  • 如果多个字段同名,它们的值会被连接为一个字符串,每个字段之间使用“逗号+空格”分隔。
2.7 XMLHttpRequest.getAllResponseHeaders()
  • 没有参数,返回一个字符串,字符串内为服务器响应的全部头信息。
  • 每个字段间用CRLF分隔(回车+换行)。
  • 未收到服务器响应或发生网络错误等,返回空字符串。

3. XMLHttpRequest 的实例属性
用于访问、设置HTTP通信过程中的相关属性,以及获得服务端响应结果。
3.1XMLHttpRequest.readyState
该变量的值是一个只读整数,表示实例的当前状态。
0
实例以生成但没调用open。
1
open已调用,send未调用,仍可使用setRequestHeader方法时。
2
send已调用且服务器已经返回了状态行与头信息。
3
表示正在接受服务器传来的数据体,即响应正文。
4
服务器的数据已经完全接受或本次接收已经失败。
由表可知这个属性并没有覆盖全过程。
通信时,每当实例对象发生状态变化时,该属性的值就会改变。
3.2 XMLHttpRequest.response,
XMLHttpRequest.responseType,

XMLHttpRequest.responseText,

XMLHttpRequest.responseXML

这四个属性储存了响应消息中响应正文相关。
  • response属性保存了服务器返回的数据体(响应正文),如果本次请求不成功或数据不完整属性等于null。
  • responseType属性是一个字符串,保存了返回的数据体(response)的数据类型。
  • responseType属性是可写的,open之后send之前,设置该值,告诉服务器希望返回的指定类型。若不设置则默认为空字符串,等同于text。上文的方法是用来覆盖响应后的数据体类型,该属性则是在请求时指定希望得到的数据类型。(但请求之后、全程都可以访问)
responseType值
含义
“”(空字符串)
等同于text。
“text”
字符串。属性为text或空时,在请求结束前(readyState=3时),response属性就已经包含了服务器已经返回的部分数据。
"blob"
Blob对象,表示服务器返回二进制对象,比如图片文件。
"arraybuffer"
ArrayBuffer对象,表示服务器返回二进制数组。
"document"
Document 对象,表示服务器返回一个文档对象。
"json"
JSON 对象。浏览器会自动调用parse方法将结果从JSON字符串转换为JSON对象,即response得到的是JSON对象。
  • 至于responseText和responseXML,在这之前需要先补充一些信息:客户端与服务端之间的数据交互是需要编码解码的,不论是responseType属性还是overrideMimeType方法,都是在告诉客户端在拿到服务端的数据后,按照哪种格式来解码,解码后将的到的内容储存在response属性中,即上表含义中表述的样。
  • 而responseText则是不看Type的值,将得到的数据按照字符串的类型解析并保存在该属性中,也就是说如果数据是一个二进制类型的结果,访问该属性大概率得到的是乱码。
  • responseXML属性也是同理,浏览器将得到的数据按照html/XML文档对象的类型进行解析,如果请求不成功或收到的数据不是这个类型的,该属性等于null。
  • 所以responseXML属性生效的前提必须是响应头信息Content-Type字段的值为text/xml或application/xml,并且responsType属性值为document,否则需要调用overrideMimeType方法强制进行XML解析,不然改属性的值为null。
  • 这两个属性都是只读,并且HTTP请求完成接收后属性才会包含完整的数据。
3.3 XMLHttpRequest.responseURL
  • 属性为字符串,表示发送数据的服务器网址。
  • 这个值不见得和open方法指定的请求网址相同,如果服务端发生跳转,这个属性返回最后实际返回数据的网址;另外,如果原始 URL 包括锚点(fragment),该属性会把锚点剥离。
3.4 XMLHttpRequest.status,
XMLHttpRequest.statusText
  • 这两个属性用于获取响应状态相关信息,status为整数储存了状态码,statusText为字符串储存了状态码的文本描述。
  • 请求发出前status值为0。
3.5 XMLHttpRequest.withCredentials
  • 布尔值,表示跨域请求时,用户信息(比如 Cookie 和认证的 HTTP 头信息)是否会包含在请求之中,默认为false,即向index.com发出跨域请求时,不会发送index.com设置在本机上的 Cookie(如果有的话)。同源请求不要设置这个属性。
  • 为了让这个属性生效,服务器必须显式返回Access-Control-Allow-Credentials:true 这个头信息。
  • withCredentials属性打开的话,跨域请求不仅会发送 Cookie,还会设置远程主机指定的 Cookie。反之也成立,如果withCredentials属性没有打开,那么跨域的 AJAX 请求即使明确要求浏览器设置 Cookie,浏览器也会忽略。
  • 注意,脚本总是遵守同源政策,无法从document.cookie或者 HTTP 回应的头信息之中,读取跨域的 Cookie,withCredentials属性不影响这一点。
3.6 XMLHttpRequest.timeout
  • 属性值为一个整数,可读写,单位为毫秒,表示多少毫秒后,若是请求还未得到结果,就会自动终止,同时触发timeout事件(如果有的话,详见后)。
  • 未设置改属性或属性值等于0,则表示没有时间限制。

4. XMLHttpRequest 实例的事件
4.0 
事件算是特殊的属性,有两种设置事件的方法:一种是通过addEventListener()方法;另一种是直接将回调函数赋值给实例对应的事件属性,属性名为事件名称前面加上on作为前缀。例如:xhr.addEventListener(‘progress’,‘updateProgress’);与xhr.onprogress=updateProgress();
4.1 readystatechange
readyState属性发生变化时触发该事件。ps:abort()方法会导致readyState属性变化从而触发该事件。
4.2 progress
progress事件会在接收服务端响应期间持续不断地周期性(通常是50毫秒)触发。
处理事件的程序会接收到event对象作为处理程序的参数,对象的target是XHR对象,具有三个额外属性:lengthComputable(进度信息是否可用,布尔值)、position(已收到的字节数)、totalSize(根据响应头确定的预期字节数),如此便可确定当前收到数据的进度。
  
    
    
  
var xhr = new XMLHttpRequest();
function updateProgress (oEvent) { if (oEvent.lengthComputable) { var percentComplete = oEvent.loaded / oEvent.total; } else { console.log('无法计算进展'); } }
xhr.addEventListener('progress', updateProgress);
xhr.open();
4.3 load、error、abort
load事件表示服务器传来的数据接收完毕,error事件表示请求出错,abort专指因abort()方法而终止连接时触发。
4.4 timeout 
服务器超出指定时间未返回结果触发timeout事件。
4.5 loadstart、loadend
loadstart事件在收到响应数据的第一个字节时触发;loadend表示HTTP通信结束,但不知道是否成功,abort、load、error三个事件任何一个事件的触发都会伴随该事件触发。
4.6 upload
  • 其实这是一个属性,只不过属性的值是一个对象,我们只能用这个对象的事件,并不能直接用这个对象。
  • XMLHttpRequest.upload属性可以得到一个对象,或者说指向一个对象,通过监听这个对象的事件可以得知客户端向服务端上传数据、文件的进度(即send方法的参数)。
  • 话句话说,send方法在进行、向服务端上传数据、文件时也是有各个阶段的,每个阶段会触发XMLHttpRequest.upload这个对象类型的属性的各种事件。
  • 该对象可被监听的事件有:loadstart、loadend、load、abort、error、progress、timeout。这些事件和上面高度重合,含义也都是高度相似的,不在赘述。

5.封装你自己的AJAX函数
显而易见地,一次AJAX通信你要些很多代码,而你一个页面会有很多次这样的AJAX通信。
所以要把一些重复的代码,封装到你自己的一个函数里,同时这个函数还要具有一定的拓展性,满足一些个别的需求。这样你每次调用你自己写的AJAX函数给函数传参即可。
这是一件很普遍也必须要做的事情,但是由于业务逻辑以及XHR实例属性方法的多种多样,这里并不能给出有概括性的、指导性的代码(如果能篇幅也会很长,你不会愿意看的)。
但这是你练习熟悉AJAX最合适不过的项目,你应该假象一个需求,去自己封装一个能实现各种复杂AJAX需求的函数,或者去网上找找别人做的demo。
【其实这个轮子已经早有人造好了,JQuery有这个方法,但你还是有必要自己去写写。

6. Navigator.sendBeacon()
用户卸载网页(就是关掉网页的意思)的时候,有时需要向服务器发一些数据。
很自然的做法是在unload事件或beforeunload事件的监听函数里面,使用XMLHttpRequest对象发送数据。但是,这样做不是很可靠,因为XMLHttpRequest对象是异步发送,很可能在它即将发送的时候,页面已经卸载了,从而导致发送取消或者发送失败。
为了防止这样,很自然的想到在这个时间里再增加一些拖时间的操作,比如设一个setTimeout啥的,或者异步改同步,但这样也有坏处,就是页面退不出去,页面卸载事件被拖长,后面页面的加载被推迟,用户体验差。

所以浏览器引入了navigator.sendBeacon()方法。这个方法还是异步发出请求,但是请求与当前页面线程脱钩,作为浏览器进程的任务,因此可以保证会把数据发出去,不拖延卸载流程。
第一个参数是目标服务端URL,第二个是请求数据。方法返回布尔值,成功发送为true,反之false。方法发送HTTP的方法是POST,可以跨域,类似表单提交数据,但不能指定回调函数。




参考:
  • 本文主要参考了阮一峰的JavaScript教程,参考了其中的叙述思路、大量说法,但很大程度上已经超出了参考,属于演绎再创作。
  • 在上面的过程中对其表述有误、模糊、繁琐的地方进行了修改,这一部分参考了《Nicholas C.Zakas,JavaScript高级程序设计》,以及大量博客。
本教程采用知识共享 署名-相同方式共享 3.0协议。
http://creativecommons.org/licenses/by-sa/3.0/

以上是关于AJAX & XMLHttpRequest的主要内容,如果未能解决你的问题,请参考以下文章

获取Ajax通信对象方法

为什么Ajax XMLHttpRequest POST方法传递参数失败了

封装ajax原理

AJAX基本属性

你真的懂 ajax 吗?

Ajax 创建 XMLHttpRequest 对象 | AJAX 教程