本文摘抄自:Ajax知识体系大梳理
地址:http://louiszhai.github.io/2016/11/02/ajax/
本文内容并不完整,请到原文阅读。
if (window.XMLHttpRequest){ // If IE7, Mozilla, Safari, etc: Use native object var xmlHttp = new XMLHttpRequest() } else { if (window.ActiveXObject){ // …otherwise, use the ActiveX control for IE5.x and IE6 var xmlHttp = new ActiveXObject(“Microsoft.XMLHTTP”); } }
XMLHttpRequest对象建立。该方案为msdn的方案。
全平台兼容方案:
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; }
该方案为网友提供的方案。css
onreadystatechange回调事件html
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表示http请求的状态, 初始值为0. 若是服务器没有显式地指定状态码, 那么status将被设置为默认值, 即200.vue
只读属性, statusText表示服务器的响应状态信息, 它是一个 UTF-16 的字符串, 请求成功且status==20X时, 返回大写的OK
. 请求失败时返回空字符串. 其余状况下返回相应的状态描述. 好比: 301的 Moved Permanently
, 302的 Found
, 303的 See Other
, 307 的 Temporary Redirect
, 400的 Bad Request
, 401的 Unauthorized
等等.html5
ProgressEvent对象具备三个重要的Read only属性.node
onprogress事件回调方法在 readyState==3
状态时开始触发, 默认传入 ProgressEvent 对象, 可经过e.loaded/e.total
来计算加载资源的进度, 该方法用于获取资源的下载进度.jquery
注意: 该方法适用于 IE10+ 及其余现代浏览器.ios
xhr.onprogress = function(e){ console.log('progress:', e.loaded/e.total); }
onload事件回调方法在ajax请求成功后触发, 触发时机在 readyState==4
状态以后.nginx
想要捕捉到一个ajax异步请求的成功状态, 而且执行回调, 通常下面的语句就足够了:git
xhr.onload = function(){ var s = xhr.status; if((s >= 200 && s < 300) || s == 304){ var resp = xhr.responseText; //TODO ... } }
ontimeout方法在ajax请求超时时触发, 经过它能够在ajax请求超时时作一些后续处理.
xhr.ontimeout = function(e) { console.error("请求超时!!!") }
均为只读属性, response表示服务器的响应内容, 相应的, responseText表示服务器响应内容的文本形式.
只读属性, responseXML表示xml形式的响应数据, 缺省为null, 若数据不是有效的xml, 则会报错.
responseType表示响应的类型, 缺省为空字符串, 可取 "arraybuffer"
, "blob"
, "document"
, "json"
, and"text"
共五种类型.
responseURL返回ajax请求最终的URL, 若是请求中存在重定向, 那么responseURL表示重定向以后的URL.
withCredentials是一个布尔值, 默认为false, 表示跨域请求中不发送cookies等信息. 当它设置为true时, cookies
,authorization headers
或者TLS客户端证书
均可以正常发送和接收. 显然它的值对同域请求没有影响.
注意: 该属性适用于 IE10+, opera12+及其余现代浏览器.
abort方法用于取消ajax请求, 取消后, readyState 状态将被设置为 0
(UNSENT
). 以下, 调用abort 方法后, 请求将被取消.
getResponseHeader方法用于获取ajax响应头中指定name的值. 若是response headers中存在相同的name, 那么它们的值将自动以字符串的形式链接在一块儿.
console.log(xhr.getResponseHeader('Content-Type'));//"text/html"
getAllResponseHeaders方法用于获取全部安全的ajax响应头, 响应头以字符串形式返回. 每一个HTTP报头名称和值用冒号分隔, 如key:value, 并以\r\n结束.
xhr.onreadystatechange = function() { if(this.readyState == this.HEADERS_RECEIVED) { console.log(this.getAllResponseHeaders()); } } //Content-Type: text/html"
以上, readyState === 2
状态时, 就意味着响应头已接受完整. 此时即可以打印出完整的 response headers.
既然能够获取响应头, 那么天然也能够设置请求头, setRequestHeader就是干这个的. 以下:
//指定请求的type为json格式 xhr.setRequestHeader("Content-type", "application/json"); //除此以外, 还能够设置其余的请求头 xhr.setRequestHeader('x-requested-with', '123456');
onerror方法用于在ajax请求出错后执行. 一般只在网络出现问题时或者ERR_CONNECTION_RESET时触发(若是请求返回的是407状态码, chrome下也会触发onerror).
upload属性默认返回一个 XMLHttpRequestUpload
对象, 用于上传资源. 该对象具备以下方法:
上述方法功能同 xhr 对象中同名方法一致. 其中, onprogress 事件回调方法可用于跟踪资源上传的进度.
xhr.upload.onprogress = function(e){ var percent = 100 * e.loaded / e.total |0; console.log('upload: ' + precent + '%'); }
overrideMimeType方法用于强制指定response 的 MIME 类型, 即强制修改response的 Content-Type
. 以下, 服务器返回的response的 MIME 类型为 text/plain
.
1
2
|
xhr.getResponseHeader(
'Content-Type');//"text/plain"
xhr.responseXML;
//null
|
经过overrideMimeType方法将response的MIME类型设置为 text/xml;charset=utf-8
, 以下所示:
1
2
|
xhr.overrideMimeType(
"text/xml; charset = utf-8");
xhr.send();
|
此时虽然 response headers 如上图, 没有变化, 但 Content-Type
已替换为新值.
1
|
xhr.getResponseHeader(
'Content-Type');//"text/xml; charset = utf-8"
|
此时, xhr.responseXML
也将返回DOM对象, 以下图.
XHR1 即 XMLHttpRequest Level 1. XHR1时, xhr对象具备以下缺点:
同源策略
限制, 只能请求同域资源.XHR2 即 XMLHttpRequest Level 2. XHR2针对XHR1的上述缺点作了以下改进:
xhr.upload.onprogress
事件回调方法获取传输进度.同源策略
限制, 这个安全机制不会变. XHR2新提供 Access-Control-Allow-Origin
等headers, 设置为 *
时表示容许任何域名请求, 从而实现跨域CORS访问(有关CORS详细介绍请耐心往下读).这里就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的.
那么问题来了, IE7, 8,9的用户怎么办? 很遗憾, 这些用户是比较尴尬的. 对于IE8,9而言, 只有一个阉割版的XDomainRequest
可用,IE7则没有. 估计IE7用户只能哭晕在厕所了.
XDomainRequest 对象是IE8,9折腾出来的, 用于支持CORS请求非成熟的解决方案. 以致于IE10中直接移除了它, 并从新回到了 XMLHttpRequest 的怀抱.
XDomainRequest 仅可用于发送 GET
和 POST
请求. 以下即建立过程.
var xdr = new XDomainRequest();
xdr具备以下属性:
以下方法:
以下事件回调:
除了缺乏一些方法外, XDomainRequest 基本上就和 XMLHttpRequest 的使用方式保持一致.
必需要明确的是:
$.ajax是jquery对原生ajax的一次封装. 经过封装ajax, jquery抹平了不一样版本浏览器异步http的差别性, 取而代之的是高度统一的api. jquery做为js类库时代的先驱, 对前端发展有着深远的影响. 了解并熟悉其ajax方法, 不可谓不重要.
$.ajax() 只有一个参数, 该参数为key-value设置对象. 实际上, jq发送的全部ajax请求, 都是经过调用该ajax方法实现的. 它的详细参数以下表:
序号 | 参数 | 类型 | 描述 |
---|---|---|---|
1 | accepts | PlainObject | 用于通知服务器该请求须要接收何种类型的返回结果. 若有必要, 推荐在$.ajaxSetup() 方法中设置一次. |
2 | async | Boolean | 默认为true, 即异步. |
3 | beforeSend | Function | 请求发送前的回调, 默认传入参数jqXHR和settings. 函数内显式返回false将取消本次请求. |
4 | cache | Boolean | 请求是否开启缓存, 默认为true, 如不须要缓存请设置为false. 不过, dataType为”script”和”jsonp”时默认为false. |
5 | complete | Function | 请求完成后的回调(请求success 和 error 以后均调用), 默认传入参数jqXHR和textStatus(请求状态, 取值为 “success”,”notmodified”,”error”,”timeout”,”abort”,”parsererror”之一). 从jq1.5开始, complete能够设置为一个包含函数的数组. 如此每一个函数将依次被调用. |
6 | contents | PlainObject | 一个以”{字符串/正则表达式}”配对的对象, 根据给定的内容类型, 解析请求的返回结果. |
7 | contentType | String | 编码类型, 相对应于http请求头域的”Content-Type”字段. 默认值为”application/x-www-form-urlencoded; charset=UTF-8”. |
8 | context | Object | 设置ajax回调函数的上下文. 默认上下文为ajax请求传入的参数设置对象. 如设置为document.body, 那么全部ajax回调函数中将以body为上下文. |
9 | converters | PlainObject | 一个数据类型到数据类型转换器的对象. 默认为 {"* text": window.String, "text html": true, "text json": jQuery.parseJSON, "text xml": jQuery.parseXML} . 如设置converters:{"json jsonp": function(msg){}} |
10 | crossDomain | Boolean | 默认同域请求为false, 跨域请求为true. |
11 | data | Object, Array | 发送到服务器的数据, 默认data为键值对格式对象, 若data为数组则按照traditional 参数的值, 自动转化为一个同名的多值查询字符串. 如{a:1,b:2}将转换为”&a=1&b=2”. |
12 | dataFilter | Function | 处理XMLHttpRequest原始响应数据的回调, 默认传入data和type参数, data是Ajax返回的原始数据, type是调用$.ajax时提供的dataType参数 |
13 | dataType | String | 预期服务器返回的数据类型, 可设置为”xml”,”html”,”script”,”json”,”jsonp”,”text”之一, 其中设置为”xml”或”text”类型时, 数据不会通过处理. |
14 | error | Function | 请求失败时的回调函数, 默认传入jqXHR(jq1.4之前为原生xhr对象),textStatus(请求状态,取值为null,”timeout”,”error”,”abort” 或 “parsererror”),errorString(错误内容), 当一个HTTP错误发生时, errorThrown 接收HTTP状态的文本部分,好比”Not Found”等. 从jq1.5开始, error能够设置为一个包含函数的数组. 如此每一个函数将依次被调用.注意: 跨域脚本和JSONP请求时error不被调用. |
15 | global | Boolean | 表示是否触发全局ajax事件, 默认为true. 设为false将再也不触发ajaxStart,ajaxStop,ajaxSend,ajaxError等. 跨站脚本和jsonp请求, 该值自动设置为false. |
16 | headers | PlainObject | 设置请求头, 格式为k-v键值对对象. 因为该设置会在beforeSend函数被调用以前生效, 所以可在beforeSend函数内覆盖该对象. |
17 | ifModified | Boolean | 只有上次请求响应改变时, 才容许请求成功. 它使用HTTP包的Last-Modified 头信息判断, 默认为false. 若设置为true, 且数据自从上次请求后没有更改过就会报错. |
18 | isLocal | Boolean | 运行当前环境设置为”本地”,默认为false, 若设置为true, 将影响请求发送时的协议. |
19 | jsonp | String | 显式指定jsonp请求中的回调函数的名称. 如jsonp:cb, jq会将cb代替callback, 以 “cb=?”传给服务器. 从jq1.5开始, 若设置jsonp:false, 那么须要明确设置jsonpCallback:”callbackName”. |
20 | jsonpCallback | String,Function | 为jsonp请求指定一个回调函数名, 以取代jq自动生成的随机函数名. 从jq1.5开始, 能够将该属性设置为一个函数, 函数的返回值就是jsonpCallback的结果. |
21 | mimeType | String | 设置一个MIME类型, 以覆盖xhr的MIM类型(jq1.5新增) |
22 | password | String | 设置认证请求中的密码 |
23 | processData | Boolean | jq的ajax方法默认会将传入的data隐式转换为查询字符串(如”&a=1&b=2”), 以配合 默认内容类型 “application/x-www-form-urlencoded”, 若是不但愿转换请设置为false. angular中想要禁用默认转换, 须要重写transformRequest方法. |
24 | scriptCharset | String | 仅在”script”请求中使用(如跨域jsonp, dataType为”script”类型). 显式指定时, 请求中将在script标签上设置charset 属性, 可在发现本地和远程编码不一致时使用. |
25 | statusCode | PlainObject | 一组http状态码和回调函数对应的键值对对象. 该对象以 {404:function(){}} 这种形式表示. 可用于根据不一样的http状态码, 执行不一样的回调.(jq1.5新增) |
26 | timeout | Number | 设置超时时间. |
27 | traditional | Boolean | 是否按照默认方式序列化data对象, 默认值为false. |
28 | type | String | 能够设置为8种http method之一, jq中不区分大小写. |
29 | url | String | 请求的uri地址. |
30 | username | String | 设置认证请求中的用户名 |
31 | xhr | Function | 在回调内建立并返回xhr对象 |
32 | xhrFields | PlainObject | 键值对对象, 用于设置原生的xhr对象, 如可用来设置withCredentials:true(jq1.5.1新增) |
$.ajax() 方法返回jqXHR对象(jq1.5起), 若是使用的不是XMLHttpRequest对象时, 如jsonp请求, 返回的jqXHR对象将尽量模拟原生的xhr. 从jq1.5起, 返回的jqXHR对象实现了promise接口, 具备以下新方法.
新方法 | 被替代的老方法(jq1.8起弃用) |
---|---|
done(function(data, textStatus, jqXHR) {}) | |
fail(function(jqXHR, textStatus, errorThrown) {}) | |
always(function(data or jqXHR, textStatus, jqXHR or errorThrown) {}) |
从jq1.6开始, done, fail, always按照FIFO队列能够分配多个回调.
$.ajax() 的转换器能够将支持的数据类型映射到其它数据类型. 若是须要将自定义数据类型映射到已知的类型. 须要使用contents
选项在响应的 “Content-Type” 和实际数据类型之间添加一个转换函数.
1
2
3
4
5
6
7
8
9
10
11
|
$.ajaxSetup({
contents: {
myContentType: /myContentType/
},
converters: {
"myContentType json": function(data) {
//TODO something
return newData;
}
}
});
|
转换一个支持的类型为自定义类型, 而后再返回. 如 text—>myContentType—>json.
1
2
3
4
5
6
7
8
9
10
11
12
|
$.ajaxSetup({
contents: {
myContentType: /myContentType/
},
converters: {
"text myContentType": true,
"myContentType json": function(data) {
//TODO something
return newData;
}
}
});
|
$.ajax()方法触发的事件纷繁复杂, 有将近20个之多. 为了囊括最多的事件, 这里以一次成功的上传请求为例, 如下是它们的调用顺序(请求出现错误时的顺序, 请自行对应).
序号 | 事件名称 | 是否全局事件 | 是否能关闭 | 默认形参 |
---|---|---|---|---|
1 | $.ajaxPrefilter | ✔️ | ❌ | function(options, originalOptions, jqXHR){} |
2 | $(document).ajaxStar | ✔️ | ✔️ | function(){}(只在当前无激活ajax时触发) |
3 | beforeSend | ❌ | - | function(jqXHR, settings){} |
4 | $(document).ajaxSend | ✔️ | ✔️ | function(){} |
5 | xhr.onloadstart | - | - | ProgressEvent |
6 | xhr.upload.onloadstart | - | - | ProgressEvent |
7 | xhr.upload.onprogress | - | - | ProgressEvent |
8 | xhr.upload.onload | - | - | ProgressEvent |
9 | xhr.upload.onloadend | - | - | ProgressEvent |
10 | xhr.onprogress | - | - | ProgressEvent |
11 | xhr.onload | - | - | ProgressEvent |
12 | ❌ | - | function(data, textStatus, jqXHR){} | |
13 | $(document).ajaxSuccess | ✔️ | ✔️ | function(event, jqXHR, options){} |
14 | ❌ | - | function(jqXHR, textStatus){} | |
15 | $(document).ajaxComplete | ✔️ | ✔️ | function(event, jqXHR, textStatus) |
16 | $(document).ajaxStop | ✔️ | ✔️ | function(){} |
17 | xhr.onloadend | - | - | ProgressEvent |
从jq1.8起, 对于函数 ajaxStart
, ajaxSend
, ajaxSuccess
, ajaxComplete
, ajaxStop
, 只能为document
对象绑定事件处理函数, 为其余元素绑定的事件处理函数不会起做用.
实际上, 若是你仅仅只是想要一个不错的http库, 相比于庞大臃肿的jquery, 短小精悍的Axios可能更加适合你. 缘由以下:
“最近团队讨论了一下, Ajax 自己跟 Vue 并无什么须要特别整合的地方, 使用 fetch polyfill 或是 axios、superagent 等等均可以起到同等的效果, vue-resource 提供的价值和其维护成本相比并不划算, 因此决定在不久之后取消对 vue-resource 的官方推荐.”
语法上Axios基本就和promise同样, 在then方法中处理回调, 在catch方法中处理异常. 以下:
1
2
3
4
5
6
7
|
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 服务 或者将其下载到本地.
说到ajax, 就不得不说起fetch, 因为篇幅较长, fetch已从本文中独立出来, 请戳 Fetch进阶指南 .
CORS是一个W3C(World Wide Web)标准, 全称是跨域资源共享(Cross-origin resource sharing).它容许浏览器向跨域服务器, 发出异步http请求, 从而克服了ajax受同源策略的限制. 实际上, 浏览器不会拦截不合法的跨域请求, 而是拦截了他们的响应, 所以即便请求不合法, 不少时候, 服务器依然收到了请求.(Chrome和Firefox下https网站不容许发送http异步请求除外)
一般, 一次跨域访问拥有以下流程:
当前几乎全部的桌面浏览器(Internet Explorer 8+, Firefox 3.5+, Safari 4+和 Chrome 3+)均可经过名为跨域资源共享的协议支持ajax跨域调用.
那么移动端兼容性又如何呢? 请看下图:
可见, CORS的技术在IOS Safari7.1及Android webview2.3中就早已支持, 即便低版本下webview的canvas在使用跨域的video或图片时会有问题, 也丝绝不影响CORS的在移动端的使用. 至此, 咱们就能够放心大胆的去应用CORS了.
1) HTTP Response Header(服务器提供):
Access-Control-Allow-Origin: 指定容许哪些源的网页发送请求.
Access-Control-Allow-Credentials: 指定是否容许cookie发送.
Access-Control-Allow-Methods: 指定容许哪些请求方法.
Access-Control-Allow-Headers: 指定容许哪些常规的头域字段, 好比说 Content-Type.
Access-Control-Expose-Headers: 指定容许哪些额外的头域字段, 好比说 X-Custom-Header.
该字段可省略. CORS请求时, xhr.getResponseHeader() 方法默认只能获取6个基本字段: Cache-Control
、Content-Language
、Content-Type
、Expires
、Last-Modified
、Pragma
. 若是须要获取其余字段, 就须要在Access-Control-Expose-Headers 中指定. 如上, 这样xhr.getResponseHeader(‘X-Custom-Header’) 才能返回X-Custom-Header字段的值.(该部分摘自阮一峰老师博客)
Access-Control-Max-Age: 指定preflight OPTIONS请求的有效期, 单位为秒.
2) HTTP Request Header(浏览器OPTIONS请求默认自带):
3) 如下全部的header name 是被拒绝的:
Proxy-
或 Sec-
开头的header nameCORS请求分为两种, ① 简单请求; ② 非简单请求.
知足以下两个条件即是简单请求, 反之则为非简单请求.(CORS请求部分摘自阮一峰老师博客)
1) 请求是如下三种之一:
2) http头域不超出如下几种字段:
application/x-www-form-urlencoded
、multipart/form-data
、text/plain
对于简单请求, 浏览器将发送一次http请求, 同时在Request头域中增长 Origin
字段, 用来标示请求发起的源, 服务器根据这个源采起不一样的响应策略. 若服务器认为该请求合法, 那么须要往返回的 HTTP Response 中添加 Access-Control-*
等字段.( Access-Control-*
相关字段解析请阅读我以前写的CORS 跨域访问 )
对于非简单请求, 好比Method为POST
且Content-Type值为 application/json
的请求或者Method为 PUT
或DELETE
的请求, 浏览器将发送两次http请求. 第一次为preflight预检(Method: OPTIONS),主要验证来源是否合法. 值得注意的是:OPTION请求响应头一样须要包含 Access-Control-*
字段等. 第二次才是真正的HTTP请求. 因此服务器必须处理OPTIONS应答(一般须要返回20X的状态码, 不然xhr.onerror事件将被触发).
以上请求流程图为:
http-equiv 至关于http的响应头, 它回应给浏览器一些有用的信息,以帮助正确和精确地显示网页内容. 以下html将容许任意域名下的网页跨域访问.
1
|
<meta http-equiv="Access-Control-Allow-Origin" content="*">
|
一般, 图片容许跨域访问, 也能够在canvas中使用跨域的图片, 但这样作会污染画布, 一旦画布受污染, 将没法读取其数据. 好比没法调用 toBlob(), toDataURL() 或 getImageData()方法. 浏览器的这种安全机制规避了未经许可的远程服务器图片被滥用的风险.(该部份内容摘自 启用了 CORS 的图片 - HTML(超文本标记语言) | MDN)
所以如需在canvas中使用跨域的图片资源, 请参考以下apache配置片断(来自HTML5 Boilerplate Apache server configs).
1
2
3
4
5
6
7
8
|
<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实现文件上传很是简单, 这里我选取原生js, jq, angular 分别来比较下, 并顺便聊聊使用它们时的注意事项.(ajax文件上传的代码已上传至github, 请戳这里预览效果: ajax 文件上传 demo | louis)
1) 为了上传文件, 咱们得先选中一个文件. 一个type为file的input框就够了.
1
|
<input id="input" type="file">
|
2) 而后用FormData对象包裹📦选中的文件.
1
2
3
|
var input = document.getElementById("input"),
formData =
new FormData();
formData.append(
"file",input.files[0]);//key能够随意定义,只要后台能理解就行
|
3) 定义上传的URL, 以及方法. github上我搭建了一个 node-webserver, 根据须要能够自行克隆下来npm start后即可调试本篇代码.
1
2
|
var url = "http://localhost:10108/test",
method =
"POST";
|
4.1) 封装一个用于发送ajax请求的方法.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
function ajax(url, method, data){
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!");
}
}
}
xhr.onerror =
function(e){
console.log(e);
}
xhr.open(method, url);
try{
setTimeout(
function(){
xhr.send(data);
});
}
catch(e){
console.log('error:',e);
}
return xhr;
}
|
4.2) 上传文件并绑定事件.
1
2
3
4
5
6
7
8
9
10
|
var xhr = ajax(url, method, formData);
xhr.upload.onprogress =
function(e){
console.log("upload progress:", e.loaded/e.total*100 + "%");
};
xhr.upload.onload =
function(){
console.log("upload onload.");
};
xhr.onload =
function(){
console.log("onload.");
}
|
上传结果以下所示:
5) fetch只要发送一个post请求, 而且body属性设置为formData便可. 遗憾的是, fetch没法跟踪上传的进度信息.
1
2
3
4
5
6
7
8
|
fetch(url, {
method: method,
body: formData
}).then(
function(res){
console.log(res);
}).catch(
function(e){
console.log(e);
});
|
jq提供了各式各样的上传插件, 其原理都是利用jq自身的ajax方法.
6) jq的ajax提供了xhr属性用于自定义各类事件.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
$.ajax({
type: method,
url: url,
data: formData,
processData : false,
contentType : false ,//必须false才会自动加上正确的Content-Type
xhr:
function(){
var xhr = $.ajaxSettings.xhr();//实际上就是return new window.XMLHttpRequest()对象
if(xhr.upload) {
xhr.upload.addEventListener(
"progress", function(e){
console.log("jq upload progress:", e.loaded/e.total*100 + "%");
},
false);
xhr.upload.addEventListener(
"load", function(){
console.log("jq upload onload.");
});
xhr.addEventListener(
"load", function(){
console.log("jq onload.");
});
return xhr;
}
}
});
|
jq上传结果以下所示:
有关jq ajax更多的api, 请参考中文文档 jQuery.ajax() | jQuery API 中文文档 .
7.1) angular提供了$http方法用于发送http请求, 该方法返回一个promise对象.
1
2
3
4
5
6
7
8
9
|
$http({
method: method,
url: url,
data: formData,
}).success(
function(res) {
console.log(res);
}).error(
function(err, status) {
console.log(err);
});
|
angular文件上传的代码已上传至github, 请戳这里预览效果: angular 文件上传 demo | louis.
低版本angular中文件上传的功能并不完整, 直到angular1.5.5才在$http中加入了eventHandler和uploadEventHandlers等方法, 使得它支持上传进度信息. 以下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
$http({
method: method,
url: url,
eventHandlers: {
progress: function(c) {//下载进度
console.log('Progress -> ' + c);
}
},
uploadEventHandlers: {
progress: function(e) {//上传进度
console.log('UploadProgress -> ' + e);
}
},
data: formData,
}).success(
function(res) {
console.log(res);
}).error(
function(err, status) {
console.log(err);
});
|
angular1.5.5如下低版本中, 请参考成熟的实现方案 angular-file-upload 以及它提供的demo Simple example .
处理二进制文件主要使用的是H5的FileReader.
PC支持性以下:
IE | Edge | Firefox | Chrome | Safari | Opera |
---|---|---|---|---|---|
10 | 12 | 3.6 | 6 | 6 | 11.5 |
Mobile支持性以下:
IOS Safari | Opera Mini | Android Browser | Chrome/Android | UC/Android |
---|---|---|---|---|
7.1 | - | 4 | 53 | 11 |
如下是其API:
属性/方法名称 | 描述 |
---|---|
error | 表示读取文件期间发生的错误. |
readyState | 表示读取文件的状态.默认有三个值:0表示文件尚未加载;1表示文件正在读取;2表示文件读取完成. |
result | 读取的文件内容. |
abort() | 取消文件读取操做, 此时readyState 属性将置为2. |
readAsArrayBuffer() | 读取文件(或blob对象)为类型化数组(ArrayBuffer), 类型化数组容许开发者以数组下标的方式, 直接操做内存, 因为数据以二进制形式传递, 效率很是高. |
读取文件(或blob对象)为二进制字符串, 该方法已移出标准api, 请谨慎使用. | |
readAsDataURL() | 读取文件(或blob对象)为base64编码的URL字符串, 与window.URL.createObjectURL方法效果相似. |
readAsText() | 读取文件(或blob对象)为文本字符串. |
onload() | 文件读取完成时的事件回调, 默认传入event事件对象. 该回调内, 可经过this.result 或 event.target.result获取读取的文件内容. |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
var xhr = new XMLHttpRequest(),
url =
"http://louiszhai.github.io/docImages/ajax01.png";
xhr.open(
"GET", url);
xhr.responseType =
"blob";
xhr.onload =
function(){
if(this.status == 200){
var blob = this.response;
var img = document.createElement("img");
//方案一
img.src =
window.URL.createObjectURL(blob);//这里blob依然占据着内存
img.onload =
function() {
window.URL.revokeObjectURL(img.src);//释放内存
};
//方案二
/*var reader = new FileReader();
reader.readAsDataURL(blob);//FileReader将返回base64编码的data-uri对象
reader.onload = function(){
img.src = this.result;
}*/
//方案三
//img.src = url;//最简单方法
document.body.appendChild(img);
}
}
xhr.send();
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
var xhr = new XMLHttpRequest();
xhr.open(
"GET","http://localhost:8080/Information/download.jsp?data=node-fetch.js");
xhr.responseType =
"blob";
xhr.onload =
function(){
if(this.status == 200){
var blob = this.response;
var reader = new FileReader();
reader.readAsBinaryString(blob);
//该方法已被移出标准api,建议使用reader.readAsText(blob);
reader.onload=
function(){
document.body.innerHTML = "<div>" + this.result + "</div>";
}
}
}
xhr.send();
|
有关二进制文件的读取, 请移步这篇博客 HTML5新特性之文件和二进制数据的操做 .
原生js可使用ES6新增的Promise. ES6的Promise基于 Promises/A+ 规范(该部分 Fetch入门指南 一文也有说起).
这里先提供一个解析responses的函数.
1
2
3
4
5
6
7
|
function todo(responses){
responses.forEach(
function(response){
response.json().then(
function(res){
console.log(res);
});
});
}
|
原生js使用 Promise.all
方法. 以下:
1
2
3
4
5
6
7
8
|
var p1 = fetch("http://localhost:10108/test1"),
p2 = fetch(
"http://localhost:10108/test2");
Promise.all([p1, p2]).then(function(responses){
todo(responses);
//TODO do somethings
});
//"test1"
//"test2"
|
jquery可使用$.when方法. 该方法接受一个或多个Deferred对象做为参数, 只有所有成功才调用resolved状态的回调函数, 但只要其中有一个失败,就调用rejected状态的回调函数. 其实, jq的Deferred是基于 Promises/A规范实现, 但并不是彻底遵循. (传送门: jQuery 中的 Deferred 和 Promises (2) ).
1
2
3
4
5
6
7
|
var p1 = $.ajax("http://localhost:10108/test1"),
p2 = $.ajax(
"http://localhost:10108/test2");
$.when(p1, p2).then(
function(res1, res2){
console.log(res1);//["test1", "success", Object]
console.log(res2);//["test2", "success", Object]
//TODO do somethings
});
|
如上, $.when默认返回一个jqXHR对象, 能够直接进行链式调用. then方法的回调中默认传入相应的请求结果, 每一个请求结果的都是数组, 数组中依次是responseText, 请求状态, 请求的jqXHR对象.
angular中能够借助 $q.all()
来实现. 别忘了, $q
须要在controller中注入. 此外, $q
相关讲解可参考 AngularJS: ng.$q 或 Angular $q service学习笔记 .
1
2
3
4
5
6
7
8
|
var p1 = fetch("http://localhost:10108/test1"),
p2 = fetch(
"http://localhost:10108/test2");
$q.all([p1, p2]).then(
function(responses){
todo(responses);
//TODO do somethings
});
//"test1"
//"test2"
|
$q.all()
实际上就是对 Promise.all
的封装.
ajax的一大痛点就是没法支持浏览器前进和后退操做. 所以早期的Gmail 采用 iframe, 来模拟ajax的前进和后退.
现在, H5普及, pjax大行其道. pajax 就是 ajax+history.pushState 组合的一种技术. 使用它即可以无刷新经过浏览器前进和后退来改变页面内容.
先看下兼容性.
IE | Edge | Firefox | Chrome | Safari | Opera | iOS Safari | Android Browser | Chrome for Android | |
---|---|---|---|---|---|---|---|---|---|
pushState/replaceState | 10 | 12 | 4 | 5 | 6 | 11.5 | 7.1 | 4.3 | 53 |
history.state | 10 | 4 | 18 | 6 | 11.5 |
可见IE8,9并不能使用 H5的history. 须要使用垫片 HTML5 History API expansion for browsers not supporting pushState, replaceState .
pjax简单易用, 仅须要以下三个api:
咱们注意到, 首次进入一个页面, 此时 history.length
值为1, history.state
为空. 以下:
1) 为了在onpopstate事件回调中每次都能拿到 history.state
, 此时须要在页面载入完成后, 自动替换下当前url.
1
|
history.replaceState(
"init", title, "xxx.html?state=0");
|
2) 每次发送ajax请求时, 在请求完成后, 调用以下, 从而实现浏览器history往前进.
1
|
history.pushState(
"ajax请求相关参数", title, "xxx.html?state=标识符");
|
3) 浏览器前进和后退时, popstate
事件会自动触发, 此时咱们手动取出 history.state
, 构建参数并从新发送ajax请求或者直接取用state值, 从而实现无刷新还原页面.
1
2
3
4
5
|
window.addEventListener("popstate", function(e) {
var currentState = history.state;
//TODO 拼接ajax请求参数并从新发送ajax请求, 从而回到历史页面
//TODO 或者从state中拿到关键值直接还原历史页面
});
|
popstate
事件触发时, 默认会传入 PopStateEvent
事件对象. 该对象具备以下属性.
若有不懂, 更详细讲解请移步 : ajax与HTML5 history pushState/replaceState实例 « 张鑫旭-鑫空间-鑫生活 .
js中的http缓存没有开关, 受制于浏览器http缓存策略. 原生xhr请求中, 可经过以下设置关闭缓存.
1
2
3
|
xhr.setRequestHeader(
"If-Modified-Since","0");
xhr.setRequestHeader(
"Cache-Control","no-cache");
//或者 URL 参数后加上 "?timestamp=" + new Date().getTime()
|
jquery的http缓存是否开启可经过在settings中指定cache.
1
2
3
4
5
6
7
|
$.ajax({
url : 'url',
dataType : "xml",
cache: true,//true表示缓存开启, false表示缓存不开启
success :
function(xml, status){
}
});
|
同时jquery还能够全局设置是否缓存. 以下将全局关闭ajax缓存.
1
|
$.ajaxSetup({
cache:false});
|
除此以外, 调试过程当中出现的浏览器缓存尤其可恶. 建议开启隐私浏览器或者勾选☑️控制台的 Disable cache
选项. (这里以Chrome举例, 其余浏览器相似)
前面已经提过, 一般只要是ajax请求收到了http状态码, 便不会进入到错误捕获里.(Chrome中407响应头除外)
实际上, $.ajax
方法略有区别, jquery的ajax方法还会在类型解析出错时触发error回调. 最多见的即是: dataType设置为json, 可是返回的data并不是json格式, 此时 $.ajax
的error回调便会触发.
有关调试, 若是接口只是作小部分修改. 那么可使用charles(Mac) 或者fiddler(Windows), 作代理, 将请求的资源替换为本地文件, 或者使用其断点功能, 直接编辑response.
若是是新增接口的调试, 能够本地搭建node服务. 利用hosts文件配置dns + nginx将http请求转发到本地node服务器. 简易的node调试服务器可参考个人 node-webserver . 以下举一个栗子🌰:
假设咱们要调试的是 www.test.com 的GET接口. 如下全部步骤以Mac为例, 其余系统, 请自行搜索🔍文件路径.
1) hosts配置.
1
2
|
sudo vim /etc/hosts
#新增一行 127.0.0.1 www.test.com
|
2) nginx 配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
brew install nginx
#安装
#安装成功后进入目标目录
cd /usr/local/etc/nginx/
cd servers #默认配置入口为nginx.conf.同时servers目录下*.conf文件已自动加入到配置文件列表中
vim test.conf
#粘贴以下内容
server {
listen 80;
server_name www.test.com;
index index.html;
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
location / {
proxy_pass http://localhost:10108/;
proxy_redirect off;
proxy_set_header Host
$host;
proxy_set_header X-Read-IP
$remote_addr;
proxy_set_header X-Forwarded-For
$proxy_add_x_forwarded_for;
}
}
#:wq保存并退出
#启动nginx
sudo nginx
-s reload #若是启动了只需重启便可
sudo nginx
#若是没有启动,便启动之
|
3) node-webServer 配置
参考 node-webserver . 启动服务前只需更改index.js, 在第9行后插入以下内容:
1
2
3
4
5
6
|
'get': {
'/': {
getKey : 'Welcome to Simple Node WebServer!'
},
'接口api': '你的response内容'//插入的代码
},
|
如需在nginx中配置CORS, 请看这里: Nginx经过CORS实现跨域.
XMLHttpRequest 返回的数据默认的字符编码是utf-8, post方法提交数据默认的字符编码也是utf-8. 若页面编码为gbk等中文编码, 那么就会产生乱码.
一般, 若是后端接口开发OK了, 前端同窗须要经过一些手段来确认接口是能正常访问的.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
curl -I -X OPTIONS -H
"Origin: http://example.com" http://localhost:10108/
# response
HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: text/json;charset=UTF-8
Access-Control-Allow-Credentials:
true
Access-Control-Allow-Headers: x-requested-with,Content-Type
Access-Control-Allow-Methods: GET,POST,PUT,DELETE,OPTIONS
Access-Control-Allow-Origin: http://example.com
Access-Control-Max-Age: 3600
Server: Node WebServer
Website: https://github.com/Louiszhai/node-webserver
Date: Fri, 21 Oct 2016 09:00:40 GMT
Connection: keep-alive
Transfer-Encoding: chunked
|
以上, http状态码为200, 表示容许OPTIONS请求.
GET, POST 请求与GET相似, 其余请求亦然.
1
2
3
4
|
curl -I -X GET -H
"Origin: http://example.com" http://localhost:10108/
#HTTP/1.1 200 OK
curl -I -X POST -H
"Origin: http://example.com" http://localhost:10108/test
#HTTP/1.1 200 OK
|
除此以外, 咱们还能够经过chrome的postman扩展进行测试. 请看postman素洁的界面:
postman支持全部类型的http请求, 因为其向chrome申请了cookie访问权限及全部http(s)网站的访问权限. 所以能够放心使用它进行各类网站api的测试.
同时, 强烈建议阅读本文的你升级postman的使用技巧, 这里有篇: 基于Postman的API自动化测试 , 拿走不谢.
移动端的支持性比较弱, 使用需谨慎. 看表.
IOS Safari | Opera Mini | Android Browser | Android Chrome | Android UC | |
---|---|---|---|---|---|
XMLHttpRequest | 8.4 | - | 4.4.4 | 53 | 11(part) |
fetch | - | - | 52 | 53 | - |
本篇为ajax而生, 通篇介绍 XMLHTTPRequest 相关的知识, 力求简明, 本欲为梳理知识, 为读者答疑解惑, 但因本人理解所限, 不免有所局限, 但愿正在阅读的你取其精华去其糟粕. 谢谢.
本文就讨论这么多内容,你们有什么问题或好的想法欢迎在下方参与留言和评论.
本文做者: louis
本文连接: http://louiszhai.github.io/2016/11/02/ajax/
参考文章