Ajax工作原理
Ajax相当于在用户和服务器之间加了一个中间层,使用户操作与服务器响应异步化。而不再将用户的所有请求都提交给服务器,像一些数据验证和数据处理等都是Ajax引擎自己来处理,只有确定需要从服务器读取新数据时再由Ajax引擎代为向服务器提交请求。这对于改善用户的体验和页面性能很有帮助。简单地说,在不需要重新刷新页面的情况下,Ajax 通过异步请求加载后台数据,并在网页上呈现出来。由于AJAX请求获取的是数据而不是HTML文档,因此它也可以节省网络带宽,让互联网用户的网络冲浪体验变得更加顺畅。
Ajax请求数据流程最核心的依赖是浏览器提供的XMLHttpRequest(XHR)对象,使得浏览器可以发出HTTP请求与接收HTTP响应。浏览器可以接着做其他事情,当web服务器的相应返回时,收到XHR返回来的数据,再使用JS回调函数和CSS相应地更新局部页面,而不用刷新整个页面,在页面与服务器交互的过程中不中断用户操作,用户甚至察觉不到浏览器正在与服务器进行通信。
XMLHttpRequest对象的属性、方法和事件
XHR属性
-
readyState
[只读属性]用于追踪xhr
当前的状态,共有 5 种可能的值,分别对应xhr
不同的阶段。
每次readyState
值变化时,都会触发xhr.onreadystatechange
事件。值 状态 描述 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
(整个数据传输过程结束)整个数据传输过程结束,不管本次请求是成功还是失败 -
status
和statusText
status
属性表示HTTP
响应状态码,如200
、302
、400
等。statusText
属性表示HTTP
响应状态的描述文本,如OK
、Not Found
等。
注意,在xhr.onload
事件中,不能简单的判断xhr.status === 200
,因为20x
、304
等HTTP
状态码也被认为是请求成功。
参考以下代码:-
xhr.onload = function () {
-
//如果请求成功
-
if((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){
-
//do successCallback
-
}
-
}
-
-
responseType
和response
可在xhr.send()
前设置responseType
,用于指定返回的响应数据类型。和xhr.overrideMimeType()
方法效果相同,推荐使用responseType
。- IE10/IE11 不支持
xhr.responseType
为json
。 -
部分浏览器不支持
xhr.responseType
为blob
。值 描述 ""
将 responseType
设为空字符串与设置为text
相同, 是默认类型 (实际上是DOMString
)。arraybuffer
response
是一个包含二进制数据的JavaScript ArrayBuffer
。blob
response
是一个包含二进制数据的 Blob 对象 。document
response
是一个HTML Document
或XML XMLDocument
,这取决于接收到的数据的MIME
类型。使用XHR
获取HTML
请参阅 HTML in XMLHttpRequest 。json
response
是一个JavaScript
对象。这个对象是通过将接收到的数据类型视为JSON
解析得到的。text
response
是包含在DOMString
对象中的文本。moz-chunked-arraybuffer
与 arraybuffer
相似,但是数据会被接收到一个流中。使用此响应类型时,响应中的值仅在progress
事件的处理程序中可用,并且只包含上一次响应progress
事件以后收到的数据,而不是自请求发送以来收到的所有数据。在progress
事件处理时访问response
将返回到目前为止收到的数据。在progress
事件处理程序之外访问,response
的值会始终为null
。ms-stream
response
是下载流的一部分;此响应类型仅允许下载请求,仅IE
支持。
- IE10/IE11 不支持
-
responseText
- 默认值为空字符串
""
- 只有当
responseType
为text
、""
时,xhr
对象上才有此属性,此时才能调用xhr.responseText
,否则抛错。 - 只有当请求成功时,才能拿到正确值。以下
2
种情况下值都为空字符串""
:请求未完成、请求失败。
- 默认值为空字符串
-
responseXML
- 默认值为
null
- 只有当
responseType
为text
、""
、document
时,xhr
对象上才有此属性,此时才能调用xhr.responseXML
,否则抛错。 - 只有当请求成功且返回数据被正确解析时,才能拿到正确值。以下
3
种情况下值都为null
:请求未完成、请求失败、请求成功但返回数据无法被正确解析时。
- 默认值为
-
upload
-
是一个
XMLHttpRequestUpload
对象,用于收集传输信息。支持事件:onloadstart
onprogress
onabort
ontimeout
onerror
onload
onloadend
具体触发顺序及条件,参考事件章节。
其中,xhr.upload.onprogress
在上传阶段(即xhr.send()
之后,xhr.readystate=2
之前)触发,每 50ms 触发一次。可获得上传信息、进度等。
上述事件回调的参数为XMLHttpRequestEventTarget
对象,详见 事件补充。
-
-
timeout
- 单位毫秒,默认值
0
,即不设置超时。 - 计时从
onloadstart
事件触发开始(即xhr.send()
开始),以onloadend
事件触发为结束。 - 在 IE 中,只能在调用
open()
方法后send()
方法前设置。其他浏览器无此限制,但仍然从xhr.send()
方法调用计时。 - 不能为同步请求设置
timeout
,否则会报错。 - 早期较多浏览器不支持,可通过
setTImeOut
实现。
- 单位毫秒,默认值
-
withCredentials
boolean
类型,默认值false
, 用于跨域请求时将cookie
加入到request header
。xhr.withCredentials
与CORS
什么关系
我们都知道,在发同域请求时,浏览器会将cookie
自动加在request header
中。但在发送跨域请求时,cookie
并不会自动加在request header
中。
造成这个问题的原因:在CORS
标准有如下规定,默认情况下,浏览器在发送跨域请求时,不能发送任何认证信息(credentials
)如cookies
和HTTP authentication schemes
。除非xhr.withCredentials
为true
。
cookies
也是一种认证信息,在跨域请求中,client
端必须手动设置xhr.withCredentials=true
,且server
端也必须允许request
能携带认证信息(即response header
中包含Access-Control-Allow-Credentials:true
),这样浏览器才会自动将cookie
加在request header
中。
注意,一旦跨域request
能够携带认证信息,server
端一定不能将Access-Control-Allow-Origin
设置为 *,而必须设置为请求页面的域名。
以JSONPlaceHolder为数据服务,阐述Ajax及XMLHttpRequest对象的用法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<script>
//增加posts
function addPosts() {
var url = "http://localhost:3000/posts";
var xhr = new XMLHttpRequest();
xhr.addEventListener("load", function () {
//判断是否正常返回
if (xhr.status === 201) {
console.log(xhr.response);
} else {
console.log("error", xhr.status);
}
});
xhr.open("POST", url);
xhr.setRequestHeader("Content-type", "application/json");
xhr.send(JSON.stringify({ title: "newTitle", author: "newAuthor" }));
}
//addPosts();
function modifyPosts(){
var url = " http://localhost:3000/posts";
var xhr = new XMLHttpRequest();
xhr.addEventListener("load",()=> {
//判断是否正常返回
if (xhr.status === 200) {
console.log(xhr.response);
} else {
console.log("error", xhr.status);
}
});
xhr.open("PUT", url+'/5');
xhr.setRequestHeader("Content-type", "application/json");
xhr.send(JSON.stringify({ title: "BGYL", author: "_Wyhyw" }));}
//modifyPosts();
function deletePosts(){
var url = " http://localhost:3000/posts";
var xhr = new XMLHttpRequest();
xhr.addEventListener("load",()=> {
//判断是否正常返回
if (xhr.status === 200) {
console.log(xhr.response);
} else {
console.log("error", xhr.status);
}
});
xhr.open("DELETE", url+'/5');
//xhr.setRequestHeader("Content-type", "application/json");
xhr.send();}
//deletePosts();
</script>
</body>
</html>