本文实例讲述了JS XMLHttpRequest原理与使用方法。分享给大家供大家参考,具体如下:你真的会使用XMLHttpRequest吗?
看到标题时,有些同学可能会想:“我已经用xhr成功地发过很多个Ajax请求了,对它的基本操作已经算挺熟练了。” 我之前的想法和你们一样,直到最近我使用xhr时踩了不少坑儿,我才突然发现其实自己并不够了解xhr,我知道的只是最最基本的使用。
于是我决定好好地研究一番xhr的真面目,可拜读了不少博客后都不甚满意,于是我决定认真阅读一遍W3C的XMLHttpRequest标准。看完标准后我如同醍醐灌顶一般,感觉到了从未有过的清澈。这篇文章就是参考W3C的XMLHttpRequest标准和结合一些实践验证总结而来的。
看到标题时,有些同学可能会想:“我已经用xhr成功地发过很多个Ajax请求了,对它的基本操作已经算挺熟练了。” 我之前的想法和你们一样,直到最近我使用xhr时踩了不少坑儿,我才突然发现其实自己并不够了解xhr,我知道的只是最最基本的使用。
于是我决定好好地研究一番xhr的真面目,可拜读了不少博客后都不甚满意,于是我决定认真阅读一遍W3C的XMLHttpRequest标准。看完标准后我如同醍醐灌顶一般,感觉到了从未有过的清澈。这篇文章就是参考W3C的XMLHttpRequest标准和结合一些实践验证总结而来的。xhrAjaxxhrxhrxhrXMLHttpRequestXMLHttpRequestAjax和XMLHttpRequest我们通常将Ajax等同于XMLHttpRequest,但细究起来它们两个是属于不同维度的2个概念。AjaxXMLHttpRequest
以下是我认为对Ajax较为准确的解释:(摘自what is Ajax)
AJAX stands for Asynchronous JavaScript and XML. AJAX is a new technique for creating better, faster, and more interactive web applications with the help of XML, HTML, CSS, and Java Script.
AJAX is based on the following open standards:




Browser-based presentation using HTML and Cascading Style Sheets (CSS).





Data is stored in XML format and fetched from the server.





Behind-the-scenes data fetches using XMLHttpRequest objects in the browser.





JavaScript to make everything happen.



以下是我认为对Ajax较为准确的解释:(摘自what is Ajax)
AJAX stands for Asynchronous JavaScript and XML. AJAX is a new technique for creating better, faster, and more interactive web applications with the help of XML, HTML, CSS, and Java Script.Ajaxwhat is AjaxAJAX is based on the following open standards:



Browser-based presentation using HTML and Cascading Style Sheets (CSS).





Data is stored in XML format and fetched from the server.





Behind-the-scenes data fetches using XMLHttpRequest objects in the browser.





JavaScript to make everything happen.




Browser-based presentation using HTML and Cascading Style Sheets (CSS).

Browser-based presentation using HTML and Cascading Style Sheets (CSS).

Data is stored in XML format and fetched from the server.

Data is stored in XML format and fetched from the server.

Behind-the-scenes data fetches using XMLHttpRequest objects in the browser.

Behind-the-scenes data fetches using XMLHttpRequest objects in the browser.

JavaScript to make everything happen.

JavaScript to make everything happen.从上面的解释中可以知道:ajax是一种技术方案,但并不是一种新技术。它依赖的是现有的CSS/HTML/Javascript,而其中最核心的依赖是浏览器提供的XMLHttpRequest对象,是这个对象使得浏览器可以发出HTTP请求与接收HTTP响应。ajax新技术CSSHTMLJavascriptXMLHttpRequestHTTPHTTP所以我用一句话来总结两者的关系:我们使用XMLHttpRequest对象来发送一个Ajax请求。XMLHttpRequestAjaxXMLHttpRequest的发展历程XMLHttpRequest一开始只是微软浏览器提供的一个接口,后来各大浏览器纷纷效仿也提供了这个接口,再后来W3C对它进行了标准化,提出了XMLHttpRequest标准。XMLHttpRequest标准又分为Level 1和Level 2。
XMLHttpRequest Level 1主要存在以下缺点:XMLHttpRequestXMLHttpRequest标准XMLHttpRequestXMLHttpRequestLevel 1Level 2XMLHttpRequest Level 1



受同源策略的限制,不能发送跨域请求;





不能发送二进制文件(如图片、视频、音频等),只能发送纯文本数据;





在发送和获取数据的过程中,无法实时获取进度信息,只能判断是否完成;




受同源策略的限制,不能发送跨域请求;

受同源策略的限制,不能发送跨域请求;

不能发送二进制文件(如图片、视频、音频等),只能发送纯文本数据;

不能发送二进制文件(如图片、视频、音频等),只能发送纯文本数据;

在发送和获取数据的过程中,无法实时获取进度信息,只能判断是否完成;

在发送和获取数据的过程中,无法实时获取进度信息,只能判断是否完成;那么Level 2对Level 1 进行了改进,XMLHttpRequest Level 2中新增了以下功能:Level 2Level 1XMLHttpRequest Level 2



可以发送跨域请求,在服务端允许的情况下;





支持发送和接收二进制数据;





新增formData对象,支持发送表单数据;





发送和获取数据时,可以获取进度信息;





可以设置请求的超时时间;




可以发送跨域请求,在服务端允许的情况下;

可以发送跨域请求,在服务端允许的情况下;

支持发送和接收二进制数据;

支持发送和接收二进制数据;

新增formData对象,支持发送表单数据;

新增formData对象,支持发送表单数据;

发送和获取数据时,可以获取进度信息;

发送和获取数据时,可以获取进度信息;

可以设置请求的超时时间;

可以设置请求的超时时间;当然更详细的对比介绍,可以参考阮老师的这篇文章,文章中对新增的功能都有具体代码示例。阮老师的这篇文章XMLHttpRequest兼容性关于xhr的浏览器兼容性,大家可以直接查看“Can I use”这个网站提供的结果XMLHttpRequest兼容性,下面提供一个截图。xhrXMLHttpRequest兼容性从图中可以看到:



IE8/IE9、Opera Mini 完全不支持xhr对象





IE10/IE11部分支持,不支持 xhr.responseType为json





部分浏览器不支持设置请求超时,即无法使用xhr.timeout





部分浏览器不支持xhr.responseType为blob




IE8/IE9、Opera Mini 完全不支持xhr对象

IE8/IE9、Opera Mini 完全不支持xhr对象xhr

IE10/IE11部分支持,不支持 xhr.responseType为json

IE10/IE11部分支持,不支持 xhr.responseType为jsonxhr.responseTypejson

部分浏览器不支持设置请求超时,即无法使用xhr.timeout

部分浏览器不支持设置请求超时,即无法使用xhr.timeoutxhr.timeout

部分浏览器不支持xhr.responseType为blob

部分浏览器不支持xhr.responseType为blobxhr.responseTypeblob细说XMLHttpRequest如何使用先来看一段使用XMLHttpRequest发送Ajax请求的简单示例代码。XMLHttpRequestAjax
function sendAjax() {
//构造表单数据
var formData = new FormData();
formData.append('username', 'johndoe');
formData.append('id', 123456);
//创建xhr对象
var xhr = new XMLHttpRequest();
//设置xhr请求的超时时间
xhr.timeout = 3000;
//设置响应返回的数据格式
xhr.responseType = "text";
//创建一个 post 请求,采用异步
xhr.open('POST', '/server', true);
//注册相关事件回调处理函数
xhr.onload = function(e) {
if(this.status == 200||this.status == 304){
alert(this.responseText);
}
};
xhr.ontimeout = function(e) { ... };
xhr.onerror = function(e) { ... };
xhr.upload.onprogress = function(e) { ... };

//发送数据
xhr.send(formData);
}

function sendAjax() {
//构造表单数据
var formData = new FormData();
formData.append('username', 'johndoe');
formData.append('id', 123456);
//创建xhr对象
var xhr = new XMLHttpRequest();
//设置xhr请求的超时时间
xhr.timeout = 3000;
//设置响应返回的数据格式
xhr.responseType = "text";
//创建一个 post 请求,采用异步
xhr.open('POST', '/server', true);
//注册相关事件回调处理函数
xhr.onload = function(e) {
if(this.status == 200||this.status == 304){
alert(this.responseText);
}
};
xhr.ontimeout = function(e) { ... };
xhr.onerror = function(e) { ... };
xhr.upload.onprogress = function(e) { ... };

//发送数据
xhr.send(formData);
}
上面是一个使用xhr发送表单数据的示例,整个流程可以参考注释。xhr
接下来我将站在使用者的角度,以问题的形式介绍xhr的基本使用。
我对每一个问题涉及到的知识点都会进行比较细致地介绍,有些知识点可能是你平时忽略关注的。
接下来我将站在使用者的角度,以问题的形式介绍xhr的基本使用。
我对每一个问题涉及到的知识点都会进行比较细致地介绍,有些知识点可能是你平时忽略关注的。xhr如何设置request header在发送Ajax请求(实质是一个HTTP请求)时,我们可能需要设置一些请求头部信息,比如content-type、connection、cookie、accept-xxx等。xhr提供了setRequestHeader来允许我们修改请求 header。AjaxHTTPcontent-typeconnectioncookieaccept-xxxxhrsetRequestHeader
void setRequestHeader(DOMString header, DOMString value);
void setRequestHeader(DOMString header, DOMString value);void setRequestHeader(DOMString header, DOMString value);注意点:注意点



方法的第一个参数 header 大小写不敏感,即可以写成content-type,也可以写成Content-Type,甚至写成content-Type;





Content-Type的默认值与具体发送的数据类型有关,请参考本文【可以发送什么类型的数据】一节;





setRequestHeader必须在open()方法之后,send()方法之前调用,否则会抛错;





setRequestHeader可以调用多次,最终的值不会采用覆盖override的方式,而是采用追加append的方式。下面是一个示例代码:




方法的第一个参数 header 大小写不敏感,即可以写成content-type,也可以写成Content-Type,甚至写成content-Type;

方法的第一个参数 header 大小写不敏感,即可以写成content-type,也可以写成Content-Type,甚至写成content-Type;content-typeContent-Typecontent-Type

Content-Type的默认值与具体发送的数据类型有关,请参考本文【可以发送什么类型的数据】一节;

Content-Type的默认值与具体发送的数据类型有关,请参考本文【可以发送什么类型的数据】一节;Content-Type

setRequestHeader必须在open()方法之后,send()方法之前调用,否则会抛错;

setRequestHeader必须在open()方法之后,send()方法之前调用,否则会抛错;setRequestHeaderopen()send()

setRequestHeader可以调用多次,最终的值不会采用覆盖override的方式,而是采用追加append的方式。下面是一个示例代码:

setRequestHeader可以调用多次,最终的值不会采用覆盖override的方式,而是采用追加append的方式。下面是一个示例代码:setRequestHeaderoverrideappend
var client = new XMLHttpRequest();
client.open('GET', 'demo.cgi');
client.setRequestHeader('X-Test', 'one');
client.setRequestHeader('X-Test', 'two');
// 最终request header中"X-Test"为: one, two
client.send();
var client = new XMLHttpRequest();
client.open('GET', 'demo.cgi');
client.setRequestHeader('X-Test', 'one');
client.setRequestHeader('X-Test', 'two');
// 最终request header中"X-Test"为: one, two
client.send();如何获取response headerxhr提供了2个用来获取响应头部的方法:getAllResponseHeaders和getResponseHeader。前者是获取 response 中的所有header 字段,后者只是获取某个指定 header 字段的值。另外,getResponseHeader(header)的header参数不区分大小写。xhrgetAllResponseHeadersgetResponseHeadergetResponseHeader(header)header
DOMString getAllResponseHeaders();
DOMString getResponseHeader(DOMString header);
DOMString getAllResponseHeaders();
DOMString getResponseHeader(DOMString header);DOMString getAllResponseHeaders();DOMString getResponseHeader(DOMString header);这2个方法看起来简单,但却处处是坑儿。这2个方法看起来简单,但却处处是坑儿。你是否遇到过下面的坑儿?——反正我是遇到了。。。



使用getAllResponseHeaders()看到的所有response header与实际在控制台 Network 中看到的 response header 不一样





使用getResponseHeader()获取某个 header 的值时,浏览器抛错Refused to get unsafe header "XXX"




使用getAllResponseHeaders()看到的所有response header与实际在控制台 Network 中看到的 response header 不一样

使用getAllResponseHeaders()看到的所有response header与实际在控制台 Network 中看到的 response header 不一样getAllResponseHeaders()response headerNetworkresponse header

使用getResponseHeader()获取某个 header 的值时,浏览器抛错Refused to get unsafe header "XXX"

使用getResponseHeader()获取某个 header 的值时,浏览器抛错Refused to get unsafe header "XXX"getResponseHeader()headerRefused to get unsafe header "XXX"经过一番寻找最终在 Stack Overflow找到了答案。Stack Overflow找到了答案



原因1:W3C的 xhr 标准中做了限制,规定客户端无法获取 response 中的 Set-Cookie、Set-Cookie2这2个字段,无论是同域还是跨域请求;





原因2:W3C 的 cors 标准对于跨域请求也做了限制,规定对于跨域请求,客户端允许获取的response header字段只限于“simple response header”和“Access-Control-Expose-Headers” (两个名词的解释见下方)。




原因1:W3C的 xhr 标准中做了限制,规定客户端无法获取 response 中的 Set-Cookie、Set-Cookie2这2个字段,无论是同域还是跨域请求;

原因1:W3C的 xhr 标准中做了限制,规定客户端无法获取 response 中的 Set-Cookie、Set-Cookie2这2个字段,无论是同域还是跨域请求;W3C的 xhr 标准中做了限制Set-CookieSet-Cookie2

原因2:W3C 的 cors 标准对于跨域请求也做了限制,规定对于跨域请求,客户端允许获取的response header字段只限于“simple response header”和“Access-Control-Expose-Headers” (两个名词的解释见下方)。

原因2:W3C 的 cors 标准对于跨域请求也做了限制,规定对于跨域请求,客户端允许获取的response header字段只限于“simple response header”和“Access-Control-Expose-Headers” (两个名词的解释见下方)。W3C 的 cors 标准对于跨域请求也做了限制simple response headerAccess-Control-Expose-Headers
"simple response header"包括的 header 字段有:Cache-Control,Content-Language,Content-Type,Expires,Last-Modified,Pragma;
"Access-Control-Expose-Headers":首先得注意是"Access-Control-Expose-Headers"进行跨域请求时响应头部中的一个字段,对于同域请求,响应头部是没有这个字段的。这个字段中列举的 header 字段就是服务器允许暴露给客户端访问的字段。
"simple response header"包括的 header 字段有:Cache-Control,Content-Language,Content-Type,Expires,Last-Modified,Pragma;
"Access-Control-Expose-Headers":首先得注意是"Access-Control-Expose-Headers"进行跨域请求时响应头部中的一个字段,对于同域请求,响应头部是没有这个字段的。这个字段中列举的 header 字段就是服务器允许暴露给客户端访问的字段。simple response headerCache-ControlContent-LanguageContent-TypeExpiresLast-ModifiedPragmaAccess-Control-Expose-HeadersAccess-Control-Expose-Headers跨域请求所以getAllResponseHeaders()只能拿到限制以外(即被视为safe)的header字段,而不是全部字段;而调用getResponseHeader(header)方法时,header参数必须是限制以外的header字段,否则调用就会报Refused to get unsafe header的错误。getAllResponseHeaders()限制以外限制以外safegetResponseHeader(header)header限制以外限制以外Refused to get unsafe header如何指定xhr.response的数据类型有些时候我们希望xhr.response返回的就是我们想要的数据类型。比如:响应返回的数据是纯JSON字符串,但我们期望最终通过xhr.response拿到的直接就是一个 js 对象,我们该怎么实现呢?
有2种方法可以实现,一个是level 1就提供的overrideMimeType()方法,另一个是level 2才提供的xhr.responseType属性。xhr.responsexhr.responselevel 1overrideMimeType()level 2xhr.responseTypexhr.overrideMimeType()xhr.overrideMimeType()overrideMimeType是xhr level 1就有的方法,所以浏览器兼容性良好。这个方法的作用就是用来重写response的content-type,这样做有什么意义呢?比如:server 端给客户端返回了一份document或者是 xml文档,我们希望最终通过xhr.response拿到的就是一个DOM对象,那么就可以用xhr.overrideMimeType('text/xml; charset = utf-8')来实现。overrideMimeTypexhr level 1responsecontent-typedocumentxmlxhr.responseDOMxhr.overrideMimeType('text/xml; charset = utf-8')再举一个使用场景,我们都知道xhr level 1不支持直接传输blob二进制数据,那如果真要传输 blob 该怎么办呢?当时就是利用overrideMimeType方法来解决这个问题的。xhr level 1overrideMimeType下面是一个获取图片文件的代码示例:
var xhr = new XMLHttpRequest();
//向 server 端获取一张图片
xhr.open('GET', '/path/to/image.png', true);

// 这行是关键!
//将响应数据按照纯文本格式来解析,字符集替换为用户自己定义的字符集
xhr.overrideMimeType('text/plain; charset=x-user-defined');

xhr.onreadystatechange = function(e) {
if (this.readyState == 4 && this.status == 200) {
//通过 responseText 来获取图片文件对应的二进制字符串
var binStr = this.responseText;
//然后自己再想方法将逐个字节还原为二进制数据
for (var i = 0, len = binStr.length; i < len; ++i) {
var c = binStr.charCodeAt(i);
//String.fromCharCode(c & 0xff);
var byte = c & 0xff;
}
}
};

xhr.send();
var xhr = new XMLHttpRequest();
//向 server 端获取一张图片
xhr.open('GET', '/path/to/image.png', true);

// 这行是关键!
//将响应数据按照纯文本格式来解析,字符集替换为用户自己定义的字符集
xhr.overrideMimeType('text/plain; charset=x-user-defined');

xhr.onreadystatechange = function(e) {
if (this.readyState == 4 && this.status == 200) {
//通过 responseText 来获取图片文件对应的二进制字符串
var binStr = this.responseText;
//然后自己再想方法将逐个字节还原为二进制数据
for (var i = 0, len = binStr.length; i < len; ++i) {
var c = binStr.charCodeAt(i);
//String.fromCharCode(c & 0xff);
var byte = c & 0xff;
}
}
};

xhr.send();代码示例中xhr请求的是一张图片,通过将 response 的 content-type 改为'text/plain; charset=x-user-defined',使得 xhr 以纯文本格式来解析接收到的blob 数据,最终用户通过this.responseText拿到的就是图片文件对应的二进制字符串,最后再将其转换为 blob 数据。xhrresponsecontent-typexhrthis.responseTextxhr.responseTypexhr.responseTyperesponseType是xhr level 2新增的属性,用来指定xhr.response的数据类型,目前还存在些兼容性问题,可以参考本文的【XMLHttpRequest的兼容性】这一小节。那么responseType可以设置为哪些格式呢,我简单做了一个表,如下:responseTypexhr level 2xhr.responseXMLHttpRequestresponseType







xhr.response 数据类型

说明









""

String字符串

默认值(在不设置responseType时)





"text"

String字符串





"document"

Document对象

希望返回 XML 格式数据时使用





"json"

javascript 对象

存在兼容性问题,IE10/IE11不支持





"blob"

Blob对象





"arrayBuffer"

ArrayBuffer对象










xhr.response 数据类型

说明







xhr.response 数据类型

说明

值xhr.response 数据类型xhr.response说明



""

String字符串

默认值(在不设置responseType时)





"text"

String字符串





"document"

Document对象

希望返回 XML 格式数据时使用





"json"

javascript 对象

存在兼容性问题,IE10/IE11不支持





"blob"

Blob对象





"arrayBuffer"

ArrayBuffer对象





""

String字符串

默认值(在不设置responseType时)

""""String字符串String默认值(在不设置responseType时)responseType

"text"

String字符串

"text""text"String字符串String

"document"

Document对象

希望返回 XML 格式数据时使用

"document""document"Document对象Document希望返回 XML 格式数据时使用XML

"json"

javascript 对象

存在兼容性问题,IE10/IE11不支持

"json""json"javascript 对象javascript存在兼容性问题,IE10/IE11不支持

"blob"

Blob对象

"blob""blob"Blob对象Blob

"arrayBuffer"

ArrayBuffer对象

"arrayBuffer""arrayBuffer"ArrayBuffer对象ArrayBuffer下面是同样是获取一张图片的代码示例,相比xhr.overrideMimeType,用xhr.response来实现简单得多。xhr.overrideMimeTypexhr.response
var xhr = new XMLHttpRequest();
xhr.open('GET', '/path/to/image.png', true);
//可以将`xhr.responseType`设置为`"blob"`也可以设置为`" arrayBuffer"`
//xhr.responseType = 'arrayBuffer';
xhr.responseType = 'blob';

xhr.onload = function(e) {
if (this.status == 200) {
var blob = this.response;
...
}
};

xhr.send();
var xhr = new XMLHttpRequest();
xhr.open('GET', '/path/to/image.png', true);
//可以将`xhr.responseType`设置为`"blob"`也可以设置为`" arrayBuffer"`
//xhr.responseType = 'arrayBuffer';
xhr.responseType = 'blob';

xhr.onload = function(e) {
if (this.status == 200) {
var blob = this.response;
...
}
};

xhr.send();小结虽然在xhr level 2中,2者是共同存在的。但其实不难发现,xhr.responseType就是用来取代xhr.overrideMimeType()的,xhr.responseType功能强大的多,xhr.overrideMimeType()能做到的xhr.responseType都能做到。所以我们现在完全可以摒弃使用xhr.overrideMimeType()了。xhr level 2xhr.responseTypexhr.overrideMimeType()xhr.responseTypexhr.overrideMimeType()xhr.responseTypexhr.overrideMimeType()如何获取response数据xhr提供了3个属性来获取请求返回的数据,分别是:xhr.response、xhr.responseText、xhr.responseXMLxhrxhr.responsexhr.responseTextxhr.responseXML



xhr.response





默认值:空字符串""





当请求完成时,此属性才有正确的值





请求未完成时,此属性的值可能是""或者 null,具体与 xhr.responseType有关:当responseType为""或"text"时,值为"";responseType为其他值时,值为 null









xhr.responseText





默认值为空字符串""





只有当 responseType 为"text"、""时,xhr对象上才有此属性,此时才能调用xhr.responseText,否则抛错





只有当请求成功时,才能拿到正确值。以下2种情况下值都为空字符串"":请求未完成、请求失败









xhr.responseXML





默认值为 null





只有当 responseType 为"text"、""、"document"时,xhr对象上才有此属性,此时才能调用xhr.responseXML,否则抛错





只有当请求成功且返回数据被正确解析时,才能拿到正确值。以下3种情况下值都为null:请求未完成、请求失败、请求成功但返回数据无法被正确解析时








xhr.response





默认值:空字符串""





当请求完成时,此属性才有正确的值





请求未完成时,此属性的值可能是""或者 null,具体与 xhr.responseType有关:当responseType为""或"text"时,值为"";responseType为其他值时,值为 null





xhr.responsexhr.response



默认值:空字符串""





当请求完成时,此属性才有正确的值





请求未完成时,此属性的值可能是""或者 null,具体与 xhr.responseType有关:当responseType为""或"text"时,值为"";responseType为其他值时,值为 null





默认值:空字符串""

默认值:空字符串""""

当请求完成时,此属性才有正确的值

当请求完成时,此属性才有正确的值

请求未完成时,此属性的值可能是""或者 null,具体与 xhr.responseType有关:当responseType为""或"text"时,值为"";responseType为其他值时,值为 null

请求未完成时,此属性的值可能是""或者 null,具体与 xhr.responseType有关:当responseType为""或"text"时,值为"";responseType为其他值时,值为 null""nullxhr.responseTyperesponseType"""text"""responseTypenull

xhr.responseText





默认值为空字符串""





只有当 responseType 为"text"、""时,xhr对象上才有此属性,此时才能调用xhr.responseText,否则抛错





只有当请求成功时,才能拿到正确值。以下2种情况下值都为空字符串"":请求未完成、请求失败





xhr.responseTextxhr.responseText



默认值为空字符串""





只有当 responseType 为"text"、""时,xhr对象上才有此属性,此时才能调用xhr.responseText,否则抛错





只有当请求成功时,才能拿到正确值。以下2种情况下值都为空字符串"":请求未完成、请求失败





默认值为空字符串""

默认值为空字符串""""

只有当 responseType 为"text"、""时,xhr对象上才有此属性,此时才能调用xhr.responseText,否则抛错

只有当 responseType 为"text"、""时,xhr对象上才有此属性,此时才能调用xhr.responseText,否则抛错responseType"text"""xhrxhr.responseText

只有当请求成功时,才能拿到正确值。以下2种情况下值都为空字符串"":请求未完成、请求失败

只有当请求成功时,才能拿到正确值。以下2种情况下值都为空字符串"":请求未完成、请求失败""

xhr.responseXML





默认值为 null





只有当 responseType 为"text"、""、"document"时,xhr对象上才有此属性,此时才能调用xhr.responseXML,否则抛错





只有当请求成功且返回数据被正确解析时,才能拿到正确值。以下3种情况下值都为null:请求未完成、请求失败、请求成功但返回数据无法被正确解析时





xhr.responseXMLxhr.responseXML



默认值为 null





只有当 responseType 为"text"、""、"document"时,xhr对象上才有此属性,此时才能调用xhr.responseXML,否则抛错





只有当请求成功且返回数据被正确解析时,才能拿到正确值。以下3种情况下值都为null:请求未完成、请求失败、请求成功但返回数据无法被正确解析时





默认值为 null

默认值为 nullnull

只有当 responseType 为"text"、""、"document"时,xhr对象上才有此属性,此时才能调用xhr.responseXML,否则抛错

只有当 responseType 为"text"、""、"document"时,xhr对象上才有此属性,此时才能调用xhr.responseXML,否则抛错responseType"text""""document"xhrxhr.responseXML

只有当请求成功且返回数据被正确解析时,才能拿到正确值。以下3种情况下值都为null:请求未完成、请求失败、请求成功但返回数据无法被正确解析时

只有当请求成功且返回数据被正确解析时,才能拿到正确值。以下3种情况下值都为null:请求未完成、请求失败、请求成功但返回数据无法被正确解析时null如何追踪ajax请求的当前状态在发一个ajax请求后,如果想追踪请求当前处于哪种状态,该怎么做呢?ajax用xhr.readyState这个属性即可追踪到。这个属性是只读属性,总共有5种可能值,分别对应xhr不同的不同阶段。每次xhr.readyState的值发生变化时,都会触发xhr.onreadystatechange事件,我们可以在这个事件中进行相关状态判断。xhr.readyStatexhrxhr.readyStatexhr.onreadystatechange
xhr.onreadystatechange = function () {
switch(xhr.readyState){
case 1://OPENED
//do something

break;
case 2://HEADERS_RECEIVED
//do something
break;
case 3://LOADING
//do something
break;
case 4://DONE
//do something
break;
}
xhr.onreadystatechange = function () {
switch(xhr.readyState){
case 1://OPENED
//do something

break;
case 2://HEADERS_RECEIVED
//do something
break;
case 3://LOADING
//do something
break;
case 4://DONE
//do something
break;
}







状态

描述









0

UNSENT (初始状态,未打开)

此时xhr对象被成功构造,open()方法还未被调用





1

OPENED (已打开,未发送)

open()方法已被成功调用,send()方法还未被调用。注意:只有xhr处于OPENED状态,才能调用xhr.setRequestHeader()和xhr.send(),否则会报错





2

HEADERS_RECEIVED (已获取响应头)

send()方法已经被调用, 响应头和响应状态已经返回





3

LOADING (正在下载响应体)

响应体(response entity body)正在下载中,此状态下通过xhr.response可能已经有了响应数据





4

DONE (整个数据传输过程结束)

整个数据传输过程结束,不管本次请求是成功还是失败










状态

描述







状态

描述

值状态描述



0

UNSENT (初始状态,未打开)

此时xhr对象被成功构造,open()方法还未被调用





1

OPENED (已打开,未发送)

open()方法已被成功调用,send()方法还未被调用。注意:只有xhr处于OPENED状态,才能调用xhr.setRequestHeader()和xhr.send(),否则会报错





2

HEADERS_RECEIVED (已获取响应头)

send()方法已经被调用, 响应头和响应状态已经返回





3

LOADING (正在下载响应体)

响应体(response entity body)正在下载中,此状态下通过xhr.response可能已经有了响应数据





4

DONE (整个数据传输过程结束)

整个数据传输过程结束,不管本次请求是成功还是失败





0

UNSENT (初始状态,未打开)

此时xhr对象被成功构造,open()方法还未被调用

00UNSENT (初始状态,未打开)UNSENT此时xhr对象被成功构造,open()方法还未被调用xhropen()

1

OPENED (已打开,未发送)

open()方法已被成功调用,send()方法还未被调用。注意:只有xhr处于OPENED状态,才能调用xhr.setRequestHeader()和xhr.send(),否则会报错

11OPENED (已打开,未发送)OPENEDopen()方法已被成功调用,send()方法还未被调用。注意:只有xhr处于OPENED状态,才能调用xhr.setRequestHeader()和xhr.send(),否则会报错open()send()xhrOPENEDxhr.setRequestHeader()xhr.send()

2

HEADERS_RECEIVED (已获取响应头)

send()方法已经被调用, 响应头和响应状态已经返回

22HEADERS_RECEIVED (已获取响应头)HEADERS_RECEIVEDsend()方法已经被调用, 响应头和响应状态已经返回send()

3

LOADING (正在下载响应体)

响应体(response entity body)正在下载中,此状态下通过xhr.response可能已经有了响应数据

33LOADING (正在下载响应体)LOADING响应体(response entity body)正在下载中,此状态下通过xhr.response可能已经有了响应数据response entity bodyxhr.response

4

DONE (整个数据传输过程结束)

整个数据传输过程结束,不管本次请求是成功还是失败

44DONE (整个数据传输过程结束)DONE整个数据传输过程结束,不管本次请求是成功还是失败如何设置请求的超时时间如果请求过了很久还没有成功,为了不会白白占用的网络资源,我们一般会主动终止请求。XMLHttpRequest提供了timeout属性来允许设置请求的超时时间。XMLHttpRequesttimeout
xhr.timeout
xhr.timeoutxhr.timeout单位:milliseconds 毫秒
默认值:0,即不设置超时0很多同学都知道:从请求开始 算起,若超过 timeout 时间请求还没有结束(包括成功/失败),则会触发ontimeout事件,主动结束该请求。请求开始请求开始timeout【那么到底什么时候才算是请求开始 ?】
——xhr.onloadstart事件触发的时候,也就是你调用xhr.send()方法的时候。
因为xhr.open()只是创建了一个连接,但并没有真正开始数据的传输,而xhr.send()才是真正开始了数据的传输过程。只有调用了xhr.send(),才会触发xhr.onloadstart 。请求开始请求开始xhr.onloadstartxhr.send()xhr.open()xhr.send()xhr.send()xhr.onloadstart【那么什么时候才算是请求结束 ?】
—— xhr.loadend事件触发的时候。请求结束请求结束xhr.loadend另外,还有2个需要注意的坑儿:



可以在 send()之后再设置此xhr.timeout,但计时起始点仍为调用xhr.send()方法的时刻。





当xhr为一个sync同步请求时,xhr.timeout必须置为0,否则会抛错。原因可以参考本文的【如何发一个同步请求】一节。




可以在 send()之后再设置此xhr.timeout,但计时起始点仍为调用xhr.send()方法的时刻。

可以在 send()之后再设置此xhr.timeout,但计时起始点仍为调用xhr.send()方法的时刻。send()xhr.timeoutxhr.send()

当xhr为一个sync同步请求时,xhr.timeout必须置为0,否则会抛错。原因可以参考本文的【如何发一个同步请求】一节。

当xhr为一个sync同步请求时,xhr.timeout必须置为0,否则会抛错。原因可以参考本文的【如何发一个同步请求】一节。xhrsyncxhr.timeout0如何发一个同步请求xhr默认发的是异步请求,但也支持发同步请求(当然实际开发中应该尽量避免使用)。到底是异步还是同步请求,由xhr.open()传入的async参数决定。xhrxhr.open()async
open(method, url [, async = true [, username = null [, password = null]]])
open(method, url [, async = true [, username = null [, password = null]]])open(method, url [, async = true [, username = null [, password = null]]])



method: 请求的方式,如GET/POST/HEADER等,这个参数不区分大小写





url: 请求的地址,可以是相对地址如example.php,这个相对是相对于当前网页的url路径;也可以是绝对地址如http://www.example.com/example.php





async: 默认值为true,即为异步请求,若async=false,则为同步请求




method: 请求的方式,如GET/POST/HEADER等,这个参数不区分大小写

method: 请求的方式,如GET/POST/HEADER等,这个参数不区分大小写methodGET/POST/HEADER

url: 请求的地址,可以是相对地址如example.php,这个相对是相对于当前网页的url路径;也可以是绝对地址如http://www.example.com/example.php

url: 请求的地址,可以是相对地址如example.php,这个相对是相对于当前网页的url路径;也可以是绝对地址如http://www.example.com/example.phpurlexample.php相对urlhttp://www.example.com/example.php

async: 默认值为true,即为异步请求,若async=false,则为同步请求

async: 默认值为true,即为异步请求,若async=false,则为同步请求asynctrueasync=false在我认真研读W3C 的 xhr 标准前,我总以为同步请求和异步请求只是阻塞和非阻塞的区别,其他什么事件触发、参数设置应该是一样的,事实证明我错了。W3C 的 xhr标准中关于open()方法有这样一段说明:open()
Throws an "InvalidAccessError" exception if async is false, the JavaScript global environment is a document environment, and either the timeout attribute is not zero, the withCredentials attribute is true, or the responseType attribute is not the empty string.
Throws an "InvalidAccessError" exception if async is false, the JavaScript global environment is a document environment, and either the timeout attribute is not zero, the withCredentials attribute is true, or the responseType attribute is not the empty string.从上面一段说明可以知道,当xhr为同步请求时,有如下限制:xhr



xhr.timeout必须为0





xhr.withCredentials必须为 false





xhr.responseType必须为""(注意置为"text"也不允许)




xhr.timeout必须为0

xhr.timeout必须为0xhr.timeout0

xhr.withCredentials必须为 false

xhr.withCredentials必须为 falsexhr.withCredentialsfalse

xhr.responseType必须为""(注意置为"text"也不允许)

xhr.responseType必须为""(注意置为"text"也不允许)xhr.responseType"""text"若上面任何一个限制不满足,都会抛错,而对于异步请求,则没有这些参数设置上的限制。之前说过页面中应该尽量避免使用sync同步请求,为什么呢?
因为我们无法设置请求超时时间(xhr.timeout为0,即不限时)。在不限制超时的情况下,有可能同步请求一直处于pending状态,服务端迟迟不返回响应,这样整个页面就会一直阻塞,无法响应用户的其他交互。syncxhr.timeout0pending另外,标准中并没有提及同步请求时事件触发的限制,但实际开发中我确实遇到过部分应该触发的事件并没有触发的现象。如在 chrome中,当xhr为同步请求时,在xhr.readyState由2变成3时,并不会触发 onreadystatechange事件,xhr.upload.onprogress和 xhr.onprogress事件也不会触发。xhrxhr.readyState23onreadystatechangexhr.upload.onprogressxhr.onprogress如何获取上传、下载的进度在上传或者下载比较大的文件时,实时显示当前的上传、下载进度是很普遍的产品需求。
我们可以通过onprogress事件来实时显示进度,默认情况下这个事件每50ms触发一次。需要注意的是,上传过程和下载过程触发的是不同对象的onprogress事件:onprogressonprogress



上传触发的是xhr.upload对象的 onprogress事件





下载触发的是xhr对象的onprogress事件




上传触发的是xhr.upload对象的 onprogress事件

上传触发的是xhr.upload对象的 onprogress事件xhr.uploadonprogress

下载触发的是xhr对象的onprogress事件

下载触发的是xhr对象的onprogress事件xhronprogress
xhr.onprogress = updateProgress;
xhr.upload.onprogress = updateProgress;
function updateProgress(event) {
if (event.lengthComputable) {
var completedPercent = event.loaded / event.total;
}
}
xhr.onprogress = updateProgress;
xhr.upload.onprogress = updateProgress;
function updateProgress(event) {
if (event.lengthComputable) {
var completedPercent = event.loaded / event.total;
}
}可以发送什么类型的数据
void send(data);
void send(data);xhr.send(data)的参数data可以是以下几种类型:xhr.send(data)



ArrayBuffer





Blob





Document





DOMString





FormData





null




ArrayBuffer

ArrayBufferArrayBuffer

Blob

BlobBlob

Document

DocumentDocument

DOMString

DOMStringDOMString

FormData

FormDataFormData

null

nullnull如果是 GET/HEAD请求,send()方法一般不传参或传 null。不过即使你真传入了参数,参数也最终被忽略,xhr.send(data)中的data会被置为 null.send()nullxhr.send(data)nullxhr.send(data)中data参数的数据类型会影响请求头部content-type的默认值:xhr.send(data)content-type



如果data是 Document 类型,同时也是HTML Document类型,则content-type默认值为text/html;charset=UTF-8;否则为application/xml;charset=UTF-8;





如果data是 DOMString 类型,content-type默认值为text/plain;charset=UTF-8;





如果data是 FormData 类型,content-type默认值为multipart/form-data; boundary=[xxx]





如果data是其他类型,则不会设置content-type的默认值




如果data是 Document 类型,同时也是HTML Document类型,则content-type默认值为text/html;charset=UTF-8;否则为application/xml;charset=UTF-8;

如果data是 Document 类型,同时也是HTML Document类型,则content-type默认值为text/html;charset=UTF-8;否则为application/xml;charset=UTF-8;dataDocumentHTML Documentcontent-typetext/html;charset=UTF-8application/xml;charset=UTF-8

如果data是 DOMString 类型,content-type默认值为text/plain;charset=UTF-8;

如果data是 DOMString 类型,content-type默认值为text/plain;charset=UTF-8;dataDOMStringcontent-typetext/plain;charset=UTF-8

如果data是 FormData 类型,content-type默认值为multipart/form-data; boundary=[xxx]

如果data是 FormData 类型,content-type默认值为multipart/form-data; boundary=[xxx]dataFormDatacontent-typemultipart/form-data; boundary=[xxx]

如果data是其他类型,则不会设置content-type的默认值

如果data是其他类型,则不会设置content-type的默认值datacontent-type当然这些只是content-type的默认值,但如果用xhr.setRequestHeader()手动设置了中content-type的值,以上默认值就会被覆盖。content-typexhr.setRequestHeader()content-type另外需要注意的是,若在断网状态下调用xhr.send(data)方法,则会抛错:Uncaught NetworkError: Failed to execute 'send' on 'XMLHttpRequest'。一旦程序抛出错误,如果不 catch 就无法继续执行后面的代码,所以调用 xhr.send(data)方法时,应该用 try-catch捕捉错误。xhr.send(data)Uncaught NetworkError: Failed to execute 'send' on 'XMLHttpRequest'xhr.send(data)try-catch
try{
xhr.send(data)
}catch(e) {
//doSomething...
};
try{
xhr.send(data)
}catch(e) {
//doSomething...
};xhr.withCredentials与 CORS 什么关系
我们都知道,在发同域请求时,浏览器会将cookie自动加在request header中。但大家是否遇到过这样的场景:在发送跨域请求时,cookie并没有自动加在request header中。
我们都知道,在发同域请求时,浏览器会将cookie自动加在request header中。但大家是否遇到过这样的场景:在发送跨域请求时,cookie并没有自动加在request header中。cookierequest headercookierequest header造成这个问题的原因是:在CORS标准中做了规定,默认情况下,浏览器在发送跨域请求时,不能发送任何认证信息(credentials)如"cookies"和"HTTP authentication schemes"。除非xhr.withCredentials为true(xhr对象有一个属性叫withCredentials,默认值为false)。CORScredentialscookiesHTTP authentication schemesxhr.withCredentialstruexhrwithCredentialsfalse所以根本原因是cookies也是一种认证信息,在跨域请求中,client端必须手动设置xhr.withCredentials=true,且server端也必须允许request能携带认证信息(即response header中包含Access-Control-Allow-Credentials:true),这样浏览器才会自动将cookie加在request header中。cookiesclientxhr.withCredentials=trueserverrequestresponse headerAccess-Control-Allow-Credentials:truecookierequest header另外,要特别注意一点,一旦跨域request能够携带认证信息,server端一定不能将Access-Control-Allow-Origin设置为*,而必须设置为请求页面的域名。requestserverAccess-Control-Allow-Origin*xhr相关事件事件分类xhr相关事件有很多,有时记起来还挺容易混乱。但当我了解了具体代码实现后,就容易理清楚了。下面是XMLHttpRequest的部分实现代码:xhrXMLHttpRequest
interface XMLHttpRequestEventTarget : EventTarget {
// event handlers
attribute EventHandler onloadstart;
attribute EventHandler onprogress;
attribute EventHandler onabort;
attribute EventHandler onerror;
attribute EventHandler onload;
attribute EventHandler ontimeout;
attribute EventHandler onloadend;
};

interface XMLHttpRequestUpload : XMLHttpRequestEventTarget {

};

interface XMLHttpRequest : XMLHttpRequestEventTarget {
// event handler
attribute EventHandler onreadystatechange;
readonly attribute XMLHttpRequestUpload upload;
};
interface XMLHttpRequestEventTarget : EventTarget {
// event handlers
attribute EventHandler onloadstart;
attribute EventHandler onprogress;
attribute EventHandler onabort;
attribute EventHandler onerror;
attribute EventHandler onload;
attribute EventHandler ontimeout;
attribute EventHandler onloadend;
};

interface XMLHttpRequestUpload : XMLHttpRequestEventTarget {

};

interface XMLHttpRequest : XMLHttpRequestEventTarget {
// event handler
attribute EventHandler onreadystatechange;
readonly attribute XMLHttpRequestUpload upload;
};从代码中我们可以看出:



XMLHttpRequestEventTarget接口定义了7个事件:





onloadstart





onprogress





onabort





ontimeout





onerror





onload





onloadend









每一个XMLHttpRequest里面都有一个upload属性,而upload是一个XMLHttpRequestUpload对象

标签: