AJAX
概念
浏览器与服务器之间,采用 HTTP 协议通信。用户在浏览器地址栏键入一个网址,或者通过网页表单向服务器提交内容,这时浏览器就会向服务器发出 HTTP 请求。 简单来说AJAX就是客户端给服务端传消息的工具(前后端交互) AJAX本身默认异步作为他的执行机制(也可以支持同步 取决于async的取值 false为同步 true为异步)
异步请求和同步请求的区别
同步请求:
1.发出请求后,浏览器本身不能做其他动作
2. 必须得等到请求完成返回数据之后,才会执行后续的代码,
3. JS在执行AJAX的时候,其他页面都处于不能执行(假死)状态
就等同于代码是排队执行的,只有当前面的完成了才能轮到后面的
默认异步:
1.异步请求就当发出请求的同时,浏览器可以继续做任何事,
2.Ajax发送请求并不会影响页面的加载与用户的操作
AJAX以前使用的是XML格式,现在被JSON格式所替代
XMLHttpRequest 对象
XMLHttpRequest 对象是 AJAX 的主要接口,用于浏览器与服务器之间的通信。尽管名字里面有XML 和Http ,它实际上可以使用多种协议(比如file 或ftp ),发送任何格式的数据(包括字符串和二进制)。 我们通过一个实例来解析该对象的使用方法
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function()
{
if (xhr.readyState === 4){
if (xhr.status === 200){
console.log(xhr.responseText);
} else {
console.error(xhr.statusText);
}
}
};
xhr.onerror = function (e) {
console.error(xhr.statusText);
};
xhr.open('GET', '/endpoint', true);
xhr.send(null);
当得到了服务器返回的数据,AJAX不会刷新本页面,只会对当前页面进行更新 注:AJAX不能进行跨域请求,只能向同源网址(协议,域名,端口号都相同)发送HTTP请求
XMLHttpRequest 的实例属性
XMLHttpRequest.readyState
XMLHttpRequest.readyState 返回一个整数,表示实例对象的当前状态。该属性只读。它可能返回以下值。
- 0,表示 XMLHttpRequest 实例已经生成,但是实例的
open() 方法还没有被调用。 - 1,表示
open() 方法已经调用,但是实例的send() 方法还没有调用,仍然可以使用实例的setRequestHeader() 方法,设定 HTTP 请求的头信息。 - 2,表示实例的
send() 方法已经调用,并且服务器返回的头信息和状态码已经收到。 - 3,表示正在接收服务器传来的数据体(body 部分)。这时,如果实例的
responseType 属性等于text 或者空字符串,responseText 属性就会包含已经收到的部分信息。 - 4,表示服务器返回的数据已经完全接收,或者本次接收已经失败。
var xhr = new XMLHttpRequest();
if (xhr.readyState === 4) {
} else {
}
上面代码中,xhr.readyState 等于4 时,表明脚本发出的 HTTP 请求已经完成。其他情况,都表示 HTTP 请求还在进行中。
XMLHttpRequest.onreadystatechange
XMLHttpRequest.onreadystatechange 属性指向一个监听函数。readystatechange 事件发生时(实例的readyState 属性变化),就会执行这个属性。 具体例子已经在上面有过体现,我们一般用这个属性来监听AJAX的状态码是否为4(即是否是正常的)
XMLHttpRequest.response
XMLHttpRequest.response 属性表示服务器返回的数据体(即 HTTP 回应的 body 部分)。它可能是任何数据类型,比如字符串、对象、二进制对象等等,具体的类型由XMLHttpRequest.responseType 属性决定。该属性只读。
如果本次请求没有成功或者数据不完整,该属性等于null 。但是,如果responseType 属性等于text 或空字符串,在请求没有结束之前(readyState 等于3的阶段),response 属性包含服务器已经返回的部分数据。
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
handler(xhr.response);
}
}
XMLHttpRequest.responseType
是字符串,表示服务器返回的数据额类型(可在open方法之后,send方法之前设置) XMLHttpRequest.responseType 属性可以等于以下值。
- “”(空字符串):等同于
text ,表示服务器返回文本数据。//适用于大多数情况 - “arraybuffer”:ArrayBuffer 对象,表示服务器返回二进制数组。
- “blob”:Blob 对象,表示服务器返回二进制对象。//图片文件
- “document”:Document 对象,表示服务器返回一个文档对象。//html/xml文档文件
- “json”:JSON 对象。
- “text”:字符串。
var xhr = new XMLHttpRequest();
xhr.open('GET', '/path/to/image.png', true);
xhr.responseType = 'blob';
xhr.onload = function(e) {
if (this.status === 200) {
var blob = new Blob([xhr.response], {type: 'image/png'});
var blob = xhr.response;
}
};
xhr.send();
上面这个例子就是代表返回的是一个图片文件
XMLHttpRequest.responseText
XMLHttpRequest.responseText 属性返回从服务器接收到的字符串,该属性为只读。只有 HTTP 请求完成接收以后,该属性才会包含完整的数据。 简单来说就是把所有的收到的字符串都显示出来,我这里用火狐的一个页面来显示:
XMLHttpRequest.responseURL
XMLHttpRequest.responseURL 属性是字符串,表示发送数据的服务器的网址。
XMLHttpRequest.status,XMLHttpRequest.statusText
XMLHttpRequest.status 属性返回一个整数,表示服务器回应的 HTTP 状态码。一般来说,如果通信成功的话,这个状态码是200;如果服务器没有返回状态码,那么这个属性默认是200。请求发出之前,该属性为0 。该属性只读。
- 200, OK,访问正常
- 301, Moved Permanently,永久移动
- 302, Moved temporarily,暂时移动
- 304, Not Modified,未修改
- 307, Temporary Redirect,暂时重定向
- 401, Unauthorized,未授权
- 403, Forbidden,禁止访问
- 404, Not Found,未发现指定网址
- 500, Internal Server Error,服务器发生错误
基本上,只有2xx和304的状态码,表示服务器返回是正常状态。
if (xhr.readyState === 4) {
if ( (xhr.status >= 200 && xhr.status < 300)
|| (xhr.status === 304) ) {
} else {
}
}
XMLHttpRequest.statusText 属性返回一个字符串,表示服务器发送的状态提示。不同于status 属性,该属性包含整个状态信息,比如“OK”和“Not Found”。在请求发送之前(即调用open() 方法之前),该属性的值是空字符串;如果服务器没有返回状态提示,该属性的值默认为“OK”。该属性为只读属性。
事件监听属性
XMLHttpRequest 对象可以对以下事件指定监听函数。
- XMLHttpRequest.onloadstart:loadstart 事件(HTTP 请求发出)的监听函数
- XMLHttpRequest.onprogress:progress事件(正在发送和加载数据)的监听函数
- XMLHttpRequest.onabort:abort 事件(请求中止,比如用户调用了
abort() 方法)的监听函数 - XMLHttpRequest.onerror:error 事件(请求失败)的监听函数
- XMLHttpRequest.onload:load 事件(请求成功完成)的监听函数
- XMLHttpRequest.ontimeout:timeout 事件(用户指定的时限超过了,请求还未完成)的监听函数
- XMLHttpRequest.onloadend:loadend 事件(请求完成,不管成功或失败)的监听函数
XMLHttpRequest.withCredentials
XMLHttpRequest.withCredentials 属性是一个布尔值,表示跨域请求时,用户信息(比如 Cookie 和认证的 HTTP 头信息)是否会包含在请求之中,默认为false ,即向example.com 发出跨域请求时,不会发送example.com 设置在本机上的 Cookie(如果有的话)。 如果需要跨域 AJAX 请求发送 Cookie,需要withCredentials 属性设为true 。注意,同源的请求不需要设置这个属性。
Cookie和Session
HTTP 协议是一种无状态协议,即每次服务端接收到客户端的请求时,都是一个全新的请求,服务器并不知道客户端的历史请求记录; Cookie和Session就是为了解决这个问题诞生的。
Session
在客户端第一次请求服务器的时候,服务器会生成一个Session对象,用于记录这次请求中的一些操作(例如你登录的用户名等等) 工作原理 在第一次请求的时候,服务器生成Session对象,并生成一个SessionID,并通过一个响应头的命令,要求客户端设置一个Cookie。 客户端收到响应后,会设置一个相应的Cookie(过期时间为这次会话消失) 接下来客户端每次向同一个网站发送请求时,请求头都会带上该 Cookie信息(包含 sessionId ), 然后,服务器通过读取请求头中的 Cookie 信息,获取名称为 JSESSIONID 的值,得到此次请求的 sessionId。
Cookie
Http协议中,Cookie用于判断两个请求是否来自于同一个浏览器(例如用户保持着登录的状态),由浏览器进行存储 Cookie分为浏览器Cookie和WebCookie两类。 工作过程 创建 Cookie 当接收到客户端发出的 HTTP 请求时,服务器可以发送带有响应的 Set-Cookie 标头,Cookie 通常由浏览器存储,然后将 Cookie 与 HTTP 标头一同向服务器发出请求。 会话 Cookies 会话 Cookie 有个特征,客户端关闭时 Cookie 会删除,因为它没有指定Expires或 Max-Age 指令。
但是,Web 浏览器可能会使用会话还原,这会使大多数会话 Cookie 保持永久状态,就像从未关闭过浏览器一样。 永久性 Cookies
永久性 Cookie 不会在客户端关闭时过期,而是在特定日期(Expires)或特定时间长度(Max-Age)外过期 简单来说,Cookie就是用来存储客户端的请求信息的,便于下次请求的发起。 更详细的Cookie内容将会在下一篇讲到
var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://example.com/', true);
xhr.withCredentials = true;
xhr.send(null);
为了让这个属性生效,服务器必须显式返回Access-Control-Allow-Credentials 这个头信息。
Access-Control-Allow-Credentials: true
withCredentials 属性打开的话,跨域请求不仅会发送 Cookie,还会设置远程主机指定的 Cookie。反之也成立,如果withCredentials 属性没有打开,那么跨域的 AJAX 请求即使明确要求浏览器设置 Cookie,浏览器也会忽略。
注意,脚本总是遵守同源政策,无法从document.cookie 或者 HTTP 回应的头信息之中,读取跨域的 Cookie,withCredentials 属性不影响这一点。
readyStateChange 事件
readyState 属性的值发生改变,就会触发 readyStateChange 事件。
我们可以通过onReadyStateChange 属性,指定这个事件的监听函数,对不同状态进行不同处理。尤其是当状态变为4 的时候,表示通信成功,这时回调函数就可以处理服务器传送回来的数据。
progress 事件
上传文件时,XMLHttpRequest 实例对象本身和实例的upload 属性,都有一个progress 事件,会不断返回上传的进度。
var xhr = new XMLHttpRequest();
function updateProgress (oEvent) {
if (oEvent.lengthComputable) {
var percentComplete = oEvent.loaded / oEvent.total;
} else {
console.log('无法计算进展');
}
}
xhr.addEventListener('progress', updateProgress);
xhr.open();
load 事件、error 事件、abort 事件
load 事件表示服务器传来的数据接收完毕,error 事件表示请求出错,abort 事件表示请求被中断(比如用户取消请求)。
var xhr = new XMLHttpRequest();
xhr.addEventListener('load', transferComplete);
xhr.addEventListener('error', transferFailed);
xhr.addEventListener('abort', transferCanceled);
xhr.open();
function transferComplete() {
console.log('数据接收完毕');
}
function transferFailed() {
console.log('数据接收出错');
}
function transferCanceled() {
console.log('用户取消接收');
}
loadend 事件
abort 、load 和error 这三个事件,会伴随一个loadend 事件,表示请求结束,但不知道其是否成功。
xhr.addEventListener('loadend', loadEnd);
function loadEnd(e) {
console.log('请求结束,状态未知');
}
timeout 事件
服务器超过指定时间还没有返回结果,就会触发 timeout 事件,具体的例子参见timeout 属性一节。
Navigator.sendBeacon()
用户卸载网页的时候,有时需要向服务器发一些数据。很自然的做法是在unload 事件或beforeunload 事件的监听函数里面,使用XMLHttpRequest 对象发送数据。但是,这样做不是很可靠,因为XMLHttpRequest 对象是异步发送,很可能在它即将发送的时候,页面已经卸载了,从而导致发送取消或者发送失败。
解决方法就是unload 事件里面,加一些很耗时的同步操作。这样就能留出足够的时间,保证异步 AJAX 能够发送成功。
function log() {
let xhr = new XMLHttpRequest();
xhr.open('post', '/log', true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send('foo=bar');
}
window.addEventListener('unload', function(event) {
log();
for (let i = 1; i < 10000; i++) {
for (let m = 1; m < 10000; m++) { continue; }
}
});
上面代码中,强制执行了一次双重循环,拖长了unload 事件的执行时间,导致异步 AJAX 能够发送成功。
类似的还可以使用setTimeout 。下面是追踪用户点击的例子。
const clickTime = 350;
const theLink = document.getElementById('target');
function log() {
let xhr = new XMLHttpRequest();
xhr.open('post', '/log', true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send('foo=bar');
}
theLink.addEventListener('click', function (event) {
event.preventDefault();
log();
setTimeout(function () {
window.location.href = theLink.getAttribute('href');
}, clickTime);
});
上面代码使用setTimeout ,拖延了350毫秒,才让页面跳转,因此使得异步 AJAX 有时间发出。
这些做法的共同问题是,卸载的时间被硬生生拖长了,后面页面的加载被推迟了,用户体验不好。
为了解决这个问题,浏览器引入了Navigator.sendBeacon() 方法。这个方法还是异步发出请求,但是请求与当前页面线程脱钩,作为浏览器进程的任务,因此可以保证会把数据发出去,不拖延卸载流程。
window.addEventListener('unload', logData, false);
function logData() {
navigator.sendBeacon('/log', analyticsData);
}
Navigator.sendBeacon 方法接受两个参数,第一个参数是目标服务器的 URL,第二个参数是所要发送的数据(可选),可以是任意类型(字符串、表单对象、二进制对象等等)。
navigator.sendBeacon(url, data)
这个方法的返回值是一个布尔值,成功发送数据为true ,否则为false 。
该方法发送数据的 HTTP 方法是 POST,可以跨域,类似于表单提交数据。它不能指定回调函数。
|