原生js实现ajax
ajax创建的步骤
- 实例化XMLHttpRequest(或ActiveXObject)对象
- 连接服务器
- 发送请求
- 接收服务器响应数据
- 根据响应的状态码进行做不同的处理
其中,在第4个步骤中需要引入一个回调函数,在接收到服务器响应的状态码时进行判断和处理
1.创建ajax对象
1 | var xhr = null; |
2.连接服务器
主要用到了open函数,它的三个参数分别是:
请求方式、请求地址、是否设置异步请求(true表示异步,是默认值,false表示同步,同步请求的情况很少)
1 | xhr.open("GET",url + ? + params,true); //GET方式 |
3.发送请求
请求的方式:
GET请求是通过将数据拼接到URL实现的,POST则是将数据作为发送请求的参数提交到服务器
POST在发送请求(send)之前要先设置表单提交的内容类型
GET提交信息时将请求内容拼接在URL中,所以send传入的参数为空(或者可以是null),POST就必须要把请求参数作为send的参数
1 | xhr.send(); //GET方式 |
完整点的demo: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
28function ajax(url, method, data) {
var xhr = null;
if (window.XMLHttpRequest) {
xhr = new XMLHttpRequest();
} else if (window.ActiveObject) {
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;
}
关于params
params是提交到服务器的参数,它必须经过encodeURIComponent()
进行编码,每次发送请求时都会在参数列表中拼接类似v=xx
的字符串,这样是为了拒绝生成缓存,每次都直接将请求发送到服务器
具体实现:
1 | function formatParams(data){ |
4.接收响应
- responseText:获取字符串形式的响应数据,只读
- responseXML:获取XML形式的响应数据,只读
- status和statusText:以数字和文本形式返回HTTP状态码,只读
- responseType:响应类型,缺省为空字符串,可以取
arraybuffer
、blob
、document
、json
、text
- responseURL:返回ajax请求最终的url,如果有请求过程有发生重定向,则返回重定向后的url
- getAllResponseHeader():获取所有响应报头
- getResponseHeader():查询响应中某个字段的值
readyState
属性:1
2
3
4
50:请求未初始化,open还未调用
1:服务器连接已经建立,open已经调用
2:请求已经接收,接收到头信息
3:请求处理中,接收到响应主体了
4:请求完成,响应就绪
status
状态码:1
2
3
4
51xx: 信息响应类,接收到请求并继续处理
2xx:处理成功响应类,动作被成功接收、理解和接受
3xx:重定向响应类,为了完成指定的动作,必须接受下一步处理
4xx:客户端错误,客户请求包含雨大错误或者是不能正确执行
5xx:服务端错误,服务器不能正确执行一个正确的请求
响应事件
onreadystatechange
事件
该事件的回调方法在readyState
状态改变时触发,在一个收到响应的ajax请求周期中,onreadystatechange
方法会被触发4次。因此可以像下面那样处理:
1 | xhr.onreadystatechange = function(e) { |
较为完整的:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17var xhr = false;
if (XMLHttpRequest) {
xhr = new XMLHttpRequest();
} else {
xhr = new ActiveXObject("Microsoft.XMLHTTP");
};
if (xhr) {
xhr.open("GET","./data.json",true);
xhr.send();
xhr.onreadystatechange = function() {
if (xhr.readyState == 4 && xhr.status == 200) {
console.log(JSON.parse(xhr.responseText).name);
// 解析取到的json数据
}
}
}
onloadstart
事件:
该事件回调在ajax请求发送前触发,触发的节点在readyState==1
之后,readyState==2
之前
它还默认传入一个ProgressEvent
事件进度对象,包含了3个只读属性:
- lengthComputable: boolean,默认false,表示长度是否可计算
- loaded:表示已加载资源大小。若是http下载资源,不包括headers信息
- total:表示资源总大小。若是http下载资源,不包括headers信息
onprogress
事件:
该事件在reaadyState==3
时触发,默认传入ProgressEvent
进度对象,主要是用来获取资源下载进度。
注意,本事件只适用于IE10+
onload
事件:
该事件在ajax请求成功后触发,也就是readyState==4
。
onloadend
事件:
该事件在ajax请求完成后触发,也就是readyState==4
后或者readyState==2
后。也是默认传入ProgressEvent
事件进度对象
ontimeout
事件:
请求超时时触发1
2
3
4xhr.timeout = 5;
xhr.ontimeout = function(e){
console.error('请求超时')
}
请求二进制文件
处理二进制文件主要使用的是html5的FileReader
相关属性:
- error:表示读取文件器件发送的错误
- readyState:表示读物文件的状态。0:文件未加载;1:文件正在读取;2:文件读取完成
- result:读取的文件内容
相关方法:
- readAsArrayBuffer:读取文件(或blob对象)为类型化数组(ArrayBuffer), 类型化数组允许开发者以数组下标的方式, 直接操作内存, 由于数据以二进制形式传递, 效率非常高
- readAsDataURL:读取文件(或blob对象)为base64编码的URL字符串, 与window.URL.createObjectURL方法效果类似
- readAsText:读取文件(或blob对象)为文本字符串
- onload:文件读取完成时的事件回调, 默认传入event事件对象. 该回调内, 可通过this.result 或 event.target.result获取读取的文件内容
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
26var xhr = new XMLHttpRequest(),
url = '';
xhr.open("GET", url);
xhr.responseType = "blob";
xhr.onload = function(){
if(this.status == 200) {
var blob = this.response;
var img = document.createElement("img");
//方案1
img.src = window.URL.createObjectURL(blob); //这里blob依然占据内存
img.onload = function() {
window.URL.revokeObjectURL(img.src); // 释放内存
};
// 方案2
var reader = new FileReader();
reader.readAsDataURL(blob);//FileReader将返回base64编码的data-uri对象
reader.onload = function(){
img.src = this.result;
}
// 方案3
img.src = url;
document.body.appendChild(img);
}
}
xhr.send();
xhr对象
xhr level1
- 仅支持文本数据传输,无法传输二进制数据
- 传输数据时,无进度信息提示,只提示是否完成
- 同源策略,无法请求跨域资源
- 无超时机制
xhr level2
- 支持二进制数据,可以上传文件,可以用FormData对象管理表单
- 有进度提示,可以用xhr.upload.onprogress事件回调方法获取传输进度
- 还是有同源策略限制,但是用户可以将
Access-Control-Allow-Origin
等headers设置为*
表示允许任何域名请求 - 可以设置timeout和ontimeout
xhr level2支持IE10及以上,IE8和IE9可以使用XDomainRequest
来解决
CORS
全称:跨域资源共享(Cross-origin resource sharing)
它允许浏览器向跨域服务器发出异步http请求, 从而克服了ajax受同源策略的限制
在实际通信过程中,浏览器不会拦截不合法的跨域请求,而是拦截服务器返回的响应,服务器实际上是接收到客户端发出的请求的,只是服务端向客户端发送的响应被浏览器拦截下来了
相关的headers信息
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
请求的有效期, 单位为秒.
HTTP Request Header(浏览器OPTIONS请求默认自带)
- Access-Control-Request-Method: 告知服务器,浏览器将发送哪种请求, 比如说POST.
- Access-Control-Request-Headers: 告知服务器, 浏览器将包含哪些额外的头域字段.
CORS请求
可分为简单请求和非简单请求,满足下列两个条件的就是简单请求
1)请求是以下3种之一:
- HEAD
- GET
- POST
2)http头不超出以下几种字段:
- Accept
- Accept-Language
- Content-Language
- Last-Event-ID
- Content-Type字段限三个值
application/x-www-form-urlencoded
、multipart/form-data
、text/plain
对于简单请求,浏览器将发送一次http请求,同时在Request
头域中增加Origin
字段,用来表示请求发起源,服务器根据该源采取不同的响应策略,如果服务器认为该请求合法,那么就会在返回的HTTP Response
中加入Access-Control-*
等字段
对于非简单请求,浏览器会发送两次http请求来验证源是否合法,其中第二次才是真正的http请求
一些demo
原生js上传文件并绑定事件
1 | var xhr = ajax(url, method, formDate); |
fetch上传
fetch只要发送一个POST请求,并将body属性设置为formData
即可,但是它无法跟踪上传的进度信息
1 | fetch(url, { |
jquery文件上传
1 | $.ajax({ |
参考:
Ajax知识体系大梳理
Ajax关于readyState(状态值)和status(状态码)的研究
HTML5新特性之文件和二进制数据的操作