Ajax知识体系大梳理
导读Ajax 全称 Asynchronous JavaScript and XML,即异步JS与XML. 它最早在IE5中被使用,然后由Mozilla,Apple,Google推广开来. 典型的代表应用有 Outlook Web Access,以及 GMail. 现代网页中几乎无ajax不欢. 前后端分离也正是建立在ajax异步通信的基础之上. 浏览器为ajax做了什么现代浏览器中,虽然几乎全部支持ajax,但它们的技术方案却分为两种: ① 标准浏览器通过 var xhr = new XMLHttpRequest(); ② IE浏览器通过 MSXML鉴于IE系列各种 "神级" 表现,我们先来看看IE浏览器风骚的走位. IE下的使用环境略显复杂,IE7及更高版本浏览器可以直接使用BOM的 XMLHttpRequest 对象. MSDN传送门: Native XMLHTTPRequest object. IE6及更低版本浏览器只能使用 Microsoft.XMLHTTP Microsoft.XMLHTTP.1.0 Msxml2.ServerXMLHTTP Msxml2.ServerXMLHTTP.3.0 Msxml2.ServerXMLHTTP.4.0 Msxml2.ServerXMLHTTP.5.0 Msxml2.ServerXMLHTTP.6.0 Msxml2.XMLHTTP Msxml2.XMLHTTP.3.0 Msxml2.XMLHTTP.4.0 Msxml2.XMLHTTP.5.0 Msxml2.XMLHTTP.6.0 简言之,Microsoft.XMLHTTP 已经非常老了,主要用于提供对历史遗留版本的支持,不建议使用.对于 MSXML4,它已被 MSXML6 替代; 而 MSXML5 又是专门针对office办公场景,在没有安装 Microsoft Office 2003 及更高版本办公软件的情况下,MSXML5 未必可用. 相比之下,MSXML6 具有比 MSXML3 更稳定,更高性能,更安全的优势,同时它也提供了一些 MSXML3 中没有的功能,比如说 XSD schema. 唯一遗憾的是,MSXML6 只在 vista 系统及以上才是默认支持的; 而 MSXML3 在 Win2k SP4及以上系统就是可用的. 因此一般情况下,MSXML3 可以作为 MSXML6 的优雅降级方案,我们通过指定 PorgID 为 Msxml2.XMLHTTP 即可自动映射到 Msxml2.XMLHTTP.3.0. 如下所示: var xhr = new ActiveXObject("Msxml2.XMLHTTP");// 即MSXML3,等同于如下语句 var xhr = new ActiveXObject("MSXML2.XMLHTTP.3.0"); MSDN有篇文章专门讲解了各个版本的MSXML. 传送门: Using the right version of MSXML in Internet Explorer. 亲测了 IE5,IE5.5,IE6,IE7,IE8,IE9,IE10,IE edge等浏览器,IE5及之后的浏览器均可以通过如下语句获取xhr对象: var xhr = new ActiveXObject("Msxml2.XMLHTTP");// 即MSXML3 var xhr = new ActiveXObject("Microsoft.XMLHTTP");// 很老的api,虽然浏览器支持,功能可能不完善,故不建议使用 以上,思路已经很清晰了,下面给出个全兼容的方法. 全平台兼容的XMLHttpRequest对象function getXHR(){ var xhr = null; if(window.XMLHttpRequest) { xhr = new XMLHttpRequest(); } else if (window.ActiveXObject) { try { xhr = new ActiveXObject("Msxml2.XMLHTTP"); } catch (e) { try { xhr = new ActiveXObject("Microsoft.XMLHTTP"); } catch (e) { alert("您的浏览器暂不支持Ajax!"); } } } return xhr; } ajax有没有破坏js单线程机制对于这个问题,我们先看下浏览器线程机制. 一般情况下,浏览器有如下四种线程:
那么这么多线程,它们究竟是怎么同js引擎线程交互的呢? 通常,它们的线程间交互以事件的方式发生,通过事件回调的方式予以通知. 而事件回调,又是以先进先出的方式添加到 浏览器中,js引擎线程会循环从 对于一个ajax请求,js引擎首先生成 当ajax请求被服务器响应并且收到response后,浏览器事件触发线程捕获到了ajax的回调事件 在 以上整个ajax请求过程中,有涉及到浏览器的4种线程. 其中除了 ajax与setTimeout排队问题通常,ajax 和 setTimeout 的事件回调都被同等的对待,按照顺序自动的被添加到 function ajax(url,method){ var xhr = getXHR(); xhr.onreadystatechange = function(){ console.log('xhr.readyState:' + this.readyState); } xhr.onloadstart = function(){ console.log('onloadStart'); } xhr.onload = function(){ console.log('onload'); } xhr.open(method,url,true); xhr.setRequestHeader('Cache-Control',3600); xhr.send(); } var timer = setTimeout(function(){ console.log('setTimeout'); },0); ajax('http://louiszhai.github.io/docImages/ajax01.png','GET'); 上述代码执行结果如下图:
由于ajax异步,setTimeout回调本应该最先被执行,然而实际上,一次ajax请求,并非所有的部分都是异步的,至少"readyState==1"的 XMLHttpRequest 属性解读首先在Chrome console下创建一个 XMLHttpRequest 实例对象xhr. 如下所示:
inherit试运行以下代码. var xhr = new XMLHttpRequest(),i=0; for(var key in xhr){ if(xhr.hasOwnProperty(key)){ i++; } } console.log(i);//0 console.log(XMLHttpRequest.prototype.hasOwnProperty('timeout'));//true 可见,XMLHttpRequest 实例对象没有自有属性. 实际上,它的所有属性均来自于 追根溯源,XMLHttpRequest 实例对象具有如下的继承关系. (下面以a<<b表示a继承b)
由上,xhr也具有Object等原型中的所有方法. 如toString方法. xhr.toString();//"[object XMLHttpRequest]" 通常,一个xhr实例对象拥有10个普通属性+9个方法. readyState只读属性,readyState属性记录了ajax调用过程中所有可能的状态. 它的取值简单明了,如下:
注意,readyState 是一个只读属性,想要改变它的值是不可行的. onreadystatechangeonreadystatechange事件回调方法在readystate状态改变时触发,在一个收到响应的ajax请求周期中,onreadystatechange 方法会被触发4次. 因此可以在 onreadystatechange 方法中绑定一些事件回调,比如: xhr.onreadystatechange = function(e){ if(xhr.readystate==4){ var s = xhr.status; if((s >= 200 && s < 300) || s == 304){ var resp = xhr.responseText; //TODO ... } } } 注意: onreadystatechange回调中默认会传入Event实例,如下:
status只读属性,status表示http请求的状态,初始值为0. 如果服务器没有显式地指定状态码,那么status将被设置为默认值,即200. statusText只读属性,statusText表示服务器的响应状态信息,它是一个 UTF-16 的字符串,请求成功且status==20X时,返回大写的 onloadstartonloadstart事件回调方法在ajax请求发送之前触发,触发时机在 onloadstart方法中默认将传入一个ProgressEvent事件进度对象. 如下:
ProgressEvent对象具有三个重要的Read only属性.
onprogressonprogress事件回调方法在 注意: 该方法适用于 IE10+ 及其他现代浏览器. xhr.onprogress = function(e){ console.log('progress:',e.loaded/e.total); } onloadonload事件回调方法在ajax请求成功后触发,触发时机在 想要捕捉到一个ajax异步请求的成功状态,并且执行回调,一般下面的语句就足够了: xhr.onload = function(){ var s = xhr.status; if((s >= 200 && s < 300) || s == 304){ var resp = xhr.responseText; //TODO ... } } onloadendonloadend事件回调方法在ajax请求完成后触发,触发时机在 onloadend方法中默认将传入一个ProgressEvent事件进度对象. timeouttimeout属性用于指定ajax的超时时长. 通过它可以灵活地控制ajax请求时间的上限. timeout的值满足如下规则:
xhr.timeout = 0; //不生效 xhr.timeout = '123'; //生效,值为123 xhr.timeout = '123s'; //不生效 xhr.timeout = ['123']; //生效,值为123 xhr.timeout = {a:123}; //不生效 ontimeoutontimeout方法在ajax请求超时时触发,通过它可以在ajax请求超时时做一些后续处理. xhr.ontimeout = function(e) { console.error("请求超时!!!") } response responseText均为只读属性,response表示服务器的响应内容,相应的,responseText表示服务器响应内容的文本形式. responseXML只读属性,responseXML表示xml形式的响应数据,缺省为null,若数据不是有效的xml,则会报错. responseTyperesponseType表示响应的类型,缺省为空字符串,可取 responseURLresponseURL返回ajax请求最终的URL,如果请求中存在重定向,那么responseURL表示重定向之后的URL. withCredentialswithCredentials是一个布尔值,默认为false,表示跨域请求中不发送cookies等信息. 当它设置为true时, 注意: 该属性适用于 IE10+,opera12+及其他现代浏览器. abortabort方法用于取消ajax请求,取消后,readyState 状态将被设置为
getResponseHeadergetResponseHeader方法用于获取ajax响应头中指定name的值. 如果response headers中存在相同的name,那么它们的值将自动以字符串的形式连接在一起. console.log(xhr.getResponseHeader('Content-Type'));//"text/html" getAllResponseHeadersgetAllResponseHeaders方法用于获取所有安全的ajax响应头,响应头以字符串形式返回. 每个HTTP报头名称和值用冒号分隔,如key:value,并以rn结束. xhr.onreadystatechange = function() { if(this.readyState == this.HEADERS_RECEIVED) { console.log(this.getAllResponseHeaders()); } } //Content-Type: text/html" 以上, setRequestHeader既然可以获取响应头,那么自然也可以设置请求头,setRequestHeader就是干这个的. 如下: //指定请求的type为json格式 xhr.setRequestHeader("Content-type","application/json"); //除此之外,还可以设置其他的请求头 xhr.setRequestHeader('x-requested-with','123456'); onerroronerror方法用于在ajax请求出错后执行. 通常只在网络出现问题时或者ERR_CONNECTION_RESET时触发(如果请求返回的是407状态码,chrome下也会触发onerror). uploadupload属性默认返回一个
上述方法功能同 xhr 对象中同名方法一致. 其中,onprogress 事件回调方法可用于跟踪资源上传的进度. xhr.upload.onprogress = function(e){ var percent = 100 * e.loaded / e.total |0; console.log('upload: ' + precent + '%'); } overrideMimeTypeoverrideMimeType方法用于强制指定response 的 MIME 类型,即强制修改response的
xhr.getResponseHeader('Content-Type');//"text/plain" xhr.responseXML;//null 通过overrideMimeType方法将response的MIME类型设置为 xhr.overrideMimeType("text/xml; charset = utf-8"); xhr.send(); 此时虽然 response headers 如上图,没有变化,但 xhr.getResponseHeader('Content-Type');//"text/xml; charset = utf-8" 此时,
XHR一级XHR1 即 XMLHttpRequest Level 1. XHR1时,xhr对象具有如下缺点:
XHR二级XHR2 即 XMLHttpRequest Level 2. XHR2针对XHR1的上述缺点做了如下改进:
这里就H5新增的FormData对象举个例. //可直接创建FormData实例 var data = new FormData(); data.append("name","louis"); xhr.send(data); //还可以通过传入表单DOM对象来创建FormData实例 var form = document.getElementById('form'); var data = new FormData(form); data.append("password","123456"); xhr.send(data); 目前,主流浏览器基本上都支持XHR2,除了IE系列需要IE10及更高版本. 因此IE10以下是不支持XHR2的. 那么问题来了,8,9的用户怎么办? 很遗憾,这些用户是比较尴尬的. 对于IE8,9而言,只有一个阉割版的 XDomainRequestXDomainRequest 对象是IE8,9折腾出来的,用于支持CORS请求非成熟的解决方案. 以至于IE10中直接移除了它,并重新回到了 XMLHttpRequest 的怀抱. XDomainRequest 仅可用于发送 var xdr = new XDomainRequest(); xdr具有如下属性:
如下方法:
如下事件回调:
除了缺少一些方法外,XDomainRequest 基本上就和 XMLHttpRequest 的使用方式保持一致. 必须要明确的是:
$.ajax$.ajax是jquery对原生ajax的一次封装. 通过封装ajax,jquery抹平了不同版本浏览器异步http的差异性,取而代之的是高度统一的api. jquery作为js类库时代的先驱,对前端发展有着深远的影响. 了解并熟悉其ajax方法,不可谓不重要. 参数列表$.ajax() 只有一个参数,该参数为key-value设置对象. 实际上,jq发送的所有ajax请求,都是通过调用该ajax方法实现的. 它的详细参数如下表:
支持promise$.ajax() 方法返回jqXHR对象(jq1.5起),如果使用的不是XMLHttpRequest对象时,如jsonp请求,返回的jqXHR对象将尽可能模拟原生的xhr. 从jq1.5起,返回的jqXHR对象实现了promise接口,具有如下新方法.
从jq1.6开始,done,fail,always按照FIFO队列可以分配多个回调. 使用转换器$.ajax() 的转换器可以将支持的数据类型映射到其它数据类型. 如果需要将自定义数据类型映射到已知的类型. 需要使用 $.ajaxSetup({ contents: { myContentType: /myContentType/ },converters: { "myContentType json": function(data) { //TODO something return newData; } } }); 转换一个支持的类型为自定义类型,然后再返回. 如 text—>myContentType—>json. $.ajaxSetup({ contents: { myContentType: /myContentType/ },converters: { "text myContentType": true,"myContentType json": function(data) { //TODO something return newData; } } }); 事件触发顺序$.ajax()方法触发的事件纷繁复杂,有将近20个之多. 为了囊括最多的事件,这里以一次成功的上传请求为例,以下是它们的调用顺序(请求出现错误时的顺序,请自行对应).
从jq1.8起,对于函数 Axios实际上,如果你仅仅只是想要一个不错的http库,相比于庞大臃肿的jquery,短小精悍的Axios可能更加适合你. 原因如下:
Axios大小仅12k,目前最新版本号为: 语法上Axios基本就和promise一样,在then方法中处理回调,在catch方法中处理异常. 如下: axios.get("https://api.github.com/users/louiszhai") .then(function(response){ console.log(response); }) .catch(function (error) { console.log(error); }); 除了get,它还支持post,delete,head,put,patch,request请求. 具体使用攻略,请戳这里: axios . 如需在网页上引入 Axios,可以链接CDN axios | Bootstrap中文网开源项目免费 CDN 服务 或者将其下载到本地. Fetch说到ajax,就不得不提及fetch,由于篇幅较长,fetch已从本文中独立出来,请戳 Fetch进阶指南 . ajax跨域请求什么是CORSCORS是一个W3C(World Wide Web)标准,全称是跨域资源共享(Cross-origin resource sharing).它允许浏览器向跨域服务器,发出异步http请求,从而克服了ajax受同源策略的限制. 实际上,浏览器不会拦截不合法的跨域请求,而是拦截了他们的响应,因此即使请求不合法,很多时候,服务器依然收到了请求.(Chrome和Firefox下https网站不允许发送http异步请求除外) 通常,一次跨域访问拥有如下流程:
移动端CORS兼容性当前几乎所有的桌面浏览器(Internet Explorer 8+,Firefox 3.5+,Safari 4+和 Chrome 3+)都可通过名为跨域资源共享的协议支持ajax跨域调用. 那么移动端兼容性又如何呢? 请看下图:
可见,CORS的技术在IOS Safari7.1及Android webview2.3中就早已支持,即使低版本下webview的canvas在使用跨域的video或图片时会有问题,也丝毫不影响CORS的在移动端的使用. 至此,我们就可以放心大胆的去应用CORS了. CORS有关的headers1) HTTP Response Header(服务器提供):
该字段可省略. CORS请求时,xhr.getResponseHeader() 方法默认只能获取6个基本字段:
2) HTTP Request Header(浏览器OPTIONS请求默认自带):
3) 以下所有的header name 是被拒绝的:
CORS请求CORS请求分为两种,① 简单请求; ② 非简单请求. 满足如下两个条件便是简单请求,反之则为非简单请求.(CORS请求部分摘自阮一峰老师博客) 1) 请求是以下三种之一:
2) http头域不超出以下几种字段:
对于简单请求,浏览器将发送一次http请求,同时在Request头域中增加 对于非简单请求,比如Method为 以上请求流程图为:
HTML启用CORShttp-equiv 相当于http的响应头,它回应给浏览器一些有用的信息,以帮助正确和精确地显示网页内容. 如下html将允许任意域名下的网页跨域访问. <meta http-equiv="Access-Control-Allow-Origin" content="*"> 图片启用CORS通常,图片允许跨域访问,也可以在canvas中使用跨域的图片,但这样做会污染画布,一旦画布受污染,将无法读取其数据. 比如无法调用 toBlob(),toDataURL() 或 getImageData()方法. 浏览器的这种安全机制规避了未经许可的远程服务器图片被滥用的风险.(该部分内容摘自 启用了 CORS 的图片 - HTML(超文本标记语言) | MDN) 因此如需在canvas中使用跨域的图片资源,请参考如下apache配置片段(来自HTML5 Boilerplate Apache server configs). <IfModule mod_setenvif.c> <IfModule mod_headers.c> <FilesMatch ".(cur|gif|ico|jpe?g|png|svgz?|webp)$"> SetEnvIf Origin ":" IS_CORS Header set Access-Control-Allow-Origin "*" env=IS_CORS </FilesMatch> </IfModule> </IfModule> ajax文件上传ajax实现文件上传非常简单,这里我选取原生js,jq,angular 分别来比较下,并顺便聊聊使用它们时的注意事项.(ajax文件上传的代码已上传至github,请戳这里预览效果: ajax 文件上传 demo | louis) 1) 为了上传文件,我们得先选中一个文件. 一个type为file的input框就够了. <input id="input" type="file"> 2) 然后用FormData对象包裹 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |