本文目录:
- 1.什么是AJAX
- 2.AJAX的常用属性
- 3.AJAX的常用方法
- 4.原生AJAX封装与调用
- 5.用promise手写实现AJAX
1.什么是AJAX
浏览器与服务器之间,采用 HTTP 协议通信。用户在浏览器地址栏键入一个网址,或者通过网页表单向服务器提交内容,这时浏览器就会向服务器发出 HTTP 请求。
1999年,微软公司发布 IE 浏览器5.0版,第一次引入新功能:允许 JavaScript 脚本向服务器发起 HTTP 请求。这个功能当时并没有引起注意,直到2004年 Gmail 发布和2005年 Google Map 发布,才引起广泛重视。2005年2月,AJAX 这个词第一次正式提出,它是 Asynchronous JavaScript and XML 的缩写,指的是通过 JavaScript 的异步通信,从服务器获取 XML 文档从中提取数据,再更新当前网页的对应部分,而不用刷新整个网页。后来,AJAX 这个词就成为 JavaScript 脚本发起 HTTP 通信的代名词,也就是说,只要用脚本发起通信,就可以叫做 AJAX 通信。W3C 也在2006年发布了它的国际标准。
具体来说,AJAX 包括以下几个步骤。
- 创建 XMLHttpRequest 实例
- 发出 HTTP 请求
- 接收服务器传回的数据
- 更新网页数据
概括起来,就是一句话,AJAX是异步 JavaScript 和 XML,是一种用于创建快速动态网页的技术。AJAX 通过原生的XMLHttpRequest对象发出 HTTP 请求,得到服务器返回的数据后,再进行处理。现在,服务器返回的都是 JSON 格式的数据,XML 格式已经过时了,但是 AJAX 这个名字已经成了一个通用名词,字面含义已经消失了。
XMLHttpRequest对象是 AJAX 的主要接口,用于浏览器与服务器之间的通信。尽管名字里面有XML和Http,它实际上可以使用多种协议(比如file或ftp),发送任何格式的数据(包括字符串和二进制)。
通过在后台与服务器进行少量数据交换,AJAX 可以使网页实现异步更新。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。
传统的网页(不使用 AJAX)如果需要更新内容,必需重载整个网页面。
XMLHttpRequest本身是一个构造函数,可以使用new命令生成实例。它没有任何参数。
var xhr = new XMLHttpRequest();
一旦新建实例,就可以使用open()方法指定建立 HTTP 连接的一些细节。xhr.open('GET', 'http://www.example.com/page.php', true);
上面代码指定使用 GET 方法,跟指定的服务器网址建立连接。第三个参数true,表示请求是异步的。
然后,指定回调函数,监听通信状态(readyState属性)的变化。
xhr.onreadystatechange = handleStateChange;
function handleStateChange() {
// ...
}
上面代码中,一旦XMLHttpRequest实例的状态发生变化,就会调用监听函数handleStateChange
最后使用send()方法,实际发出请求。
xhr.send(null);
上面代码中,send()的参数为null,表示发送请求的时候,不带有数据体。如果发送的是 POST 请求,这里就需要指定数据体。
一旦拿到服务器返回的数据,AJAX 不会刷新整个网页,而是只更新网页里面的相关部分,从而不打断用户正在做的事情。
注意,AJAX 只能向同源网址(协议、域名、端口都相同)发出 HTTP 请求,如果发出跨域请求,就会报错。
下面是XMLHttpRequest对象简单用法的完整例子。
1:创建Ajax对象
var xhr = window.XMLHttpRequest?new XMLHttpRequest():new ActiveXObject('Microsoft.XMLHTTP');// 兼容IE6及以下版本
如果不需要考虑兼容低版本IE,则可以直接写成下面这样:
var xhr = new XMLHttpRequest();
2:配置 Ajax请求地址
xhr.open('get','index.xml',true);
3:发送请求
xhr.send(null);
4:监听请求,接受响应
xhr.onreadysatechange=function(){
if(xhr.readySates==4&&xhr.status==200 || xhr.status==304 )
console.log(xhr.responsetXML)
}
2.AJAX的常用属性
AJAX的工作原理就是利用实例化的XMLHTTPRequest对象向服务器发出请求,然后根据服务器的响应数据进行响应的处理。
AJAX的核心是XMLHTTPRequest对象(是对象,就有属性和方法)
常用的属性:
2.1.XMLHttpRequest.readyState
XMLHttpRequest.readyState返回一个整数,表示实例对象的当前状态。该属性只读。它可能返回以下值。
- 0,表示 XMLHttpRequest 实例已经生成,但是实例的open()方法还没有被调用。
- 1,表示open()方法已经调用,但是实例的send()方法还没有调用,仍然可以使用实例的setRequestHeader()方法,设定 HTTP 请求的头信息。
- 2,表示实例的send()方法已经调用,并且服务器返回的头信息和状态码已经收到。
- 3,表示正在接收服务器传来的数据体(body 部分)。这时,如果实例的responseType属性等于text或者空字符串,responseText属性就会包含已经收到的部分信息。
- 4,表示服务器返回的数据已经完全接收,或者本次接收已经失败。
通信过程中,每当实例对象发生状态变化,它的readyState属性的值就会改变。这个值每一次变化,都会触发readyStateChange事件。
var xhr = new XMLHttpRequest();
if (xhr.readyState === 4) {
// 请求结束,处理服务器返回的数据
} else {
// 显示提示“加载中……”
}
上面代码中,xhr.readyState等于4时,表明脚本发出的 HTTP 请求已经完成。其他情况,都表示 HTTP 请求还在进行中。
2.2.XMLHttpRequest.onreadystatechange
XMLHttpRequest.onreadystatechange属性指向一个监听函数。readystatechange事件发生时(实例的readyState属性变化),就会执行这个属性。
另外,如果使用实例的abort()方法,终止 XMLHttpRequest 请求,也会造成readyState属性变化,导致调用XMLHttpRequest.onreadystatechange属性。
下面是一个例子。
var xhr = new XMLHttpRequest();
xhr.open( 'GET', 'http://example.com' , true );
xhr.onreadystatechange = function () {
if (xhr.readyState !== 4 || xhr.status !== 200) {
return;
}
console.log(xhr.responseText);
};
xhr.send();
2.3.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);
}
}
2.4.XMLHttpRequest.responseType
XMLHttpRequest.responseType属性是一个字符串,表示服务器返回数据的类型。这个属性是可写的,可以在调用open()方法之后、调用send()方法之前,设置这个属性的值,告诉服务器返回指定类型的数据。如果responseType设为空字符串,就等同于默认值text。
XMLHttpRequest.responseType属性可以等于以下值。
- ""(空字符串):等同于text,表示服务器返回文本数据。
- "arraybuffer":ArrayBuffer 对象,表示服务器返回二进制数组。
- "blob":Blob 对象,表示服务器返回二进制对象。
- "document":Document 对象,表示服务器返回一个文档对象。
- "json":JSON 对象。
- "text":字符串。
上面几种类型之中,text类型适合大多数情况,而且直接处理文本也比较方便。document类型适合返回 HTML / XML 文档的情况,这意味着,对于那些打开 CORS 的网站,可以直接用 Ajax 抓取网页,然后不用解析 HTML 字符串,直接对抓取回来的数据进行 DOM 操作。blob类型适合读取二进制数据,比如图片文件。
2.5.XMLHttpRequest.timeout,XMLHttpRequestEventTarget.ontimeout
XMLHttpRequest.timeout属性返回一个整数,表示多少毫秒后,如果请求仍然没有得到结果,就会自动终止。如果该属性等于0,就表示没有时间限制。
XMLHttpRequestEventTarget.ontimeout属性用于设置一个监听函数,如果发生 timeout 事件,就会执行这个监听函数。
下面是一个例子。
var xhr = new XMLHttpRequest();
var url = '/server';
xhr.ontimeout = function () {
console.error('The request for ' + url + ' timed out.');
};
xhr.onload = function() {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
// 处理服务器返回的数据
} else {
console.error(xhr.statusText);
}
}
};
xhr.open('GET', url, true);
// 指定 10 秒钟超时
xhr.timeout = 10 * 1000;
xhr.send(null);
3.AJAX的常用方法
3.1.XMLHttpRequest.open()
XMLHttpRequest.open()方法用于指定 HTTP 请求的参数,或者说初始化 XMLHttpRequest 实例对象。它一共可以接受五个参数。
open("method", "url", "async","user","password")
- method:表示 HTTP 动词方法,比如GET、POST、PUT、DELETE、HEAD等。
- url: 表示请求发送目标 URL。
- async: 布尔值,表示请求是否为异步,默认为true。如果设为false,则send()方法只有等到收到服务器返回了结果,才会进行下一步操作。该参数可选。由于同步 AJAX 请求会造成浏览器失去响应,许多浏览器已经禁止在主线程使用,只允许 Worker 里面使用。所以,这个参数轻易不应该设为false。
- user:表示用于认证的用户名,默认为空字符串。该参数可选。
- password:表示用于认证的密码,默认为空字符串。该参数可选。
注意,如果对使用过open()方法的 AJAX 请求,再次使用这个方法,等同于调用abort(),即终止请求。
下面发送 POST 请求的例子。
var xhr = new XMLHttpRequest();
xhr.open('POST', encodeURI('someURL'));
3.2.XMLHttpRequest.send()
XMLHttpRequest.send()方法用于实际发出 HTTP 请求。它的参数是可选的,如果不带参数,就表示 HTTP 请求只有一个 URL,没有数据体,典型例子就是 GET 请求;如果带有参数,就表示除了头信息,还带有包含具体数据的信息体,典型例子就是 POST 请求。
下面是 GET 请求的例子。
var xhr = new XMLHttpRequest();
xhr.open('GET',
'http://www.example.com/?id=' + encodeURIComponent(id),
true
);
xhr.send(null);
上面代码中,GET请求的参数,作为查询字符串附加在 URL 后面。
下面是发送 POST 请求的例子。
var xhr = new XMLHttpRequest();
var data = 'email='
+ encodeURIComponent(email)
+ '&password='
+ encodeURIComponent(password);
xhr.open('POST', 'http://www.example.com', true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send(data);
注意,所有 XMLHttpRequest 的监听事件,都必须在send()方法调用之前设定。
send方法的参数就是发送的数据。多种格式的数据,都可以作为它的参数。
void send();
void send(ArrayBufferView data);
void send(Blob data);
void send(Document data);
void send(String data);
void send(FormData data);
如果send()发送 DOM 对象,在发送之前,数据会先被串行化。如果发送二进制数据,最好是发送ArrayBufferView或Blob对象,这使得通过 Ajax 上传文件成为可能。
下面是发送表单数据的例子。FormData对象可以用于构造表单数据。
var formData = new FormData();
formData.append('username', '张三');
formData.append('email', 'zhangsan@example.com');
formData.append('birthDate', 1940);
var xhr = new XMLHttpRequest();
xhr.open('POST', '/register');
xhr.send(formData);
上面代码中,FormData对象构造了表单数据,然后使用send()方法发送。它的效果与发送下面的表单数据是一样的。
<form id='registration' name='registration' action='/register'>
<input type='text' name='username' value='张三'>
<input type='email' name='email' value='zhangsan@example.com'>
<input type='number' name='birthDate' value='1940'>
<input type='submit' onclick='return sendForm(this.form);'>
</form>
3.3.readyStateChange 事件
readyState属性的值发生改变,就会触发 readyStateChange 事件。
我们可以通过onReadyStateChange属性,指定这个事件的监听函数,对不同状态进行不同处理。尤其是当状态变为4的时候,表示通信成功,这时回调函数就可以处理服务器传送回来的数据。
3.4.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('用户取消接收');
}
3.5.timeout 事件
服务器超过指定时间还没有返回结果,就会触发 timeout 事件。
4.原生AJAX封装与调用
function ajax(options) {
options = options || {};
options.type = (options.type || "GET").toUpperCase();
options.dataType = options.dataType || 'json';
options.async = options.async || true;
options.timeout=options.timeout||5000;//超时处理,默认5s
var params = getParams(options.data);
var timeoutFlag=null;
var xhr;
var that=this;
if (window.XMLHttpRequest) {
xhr = new XMLHttpRequest();
} else {
//为了兼容ie6以下
xhr = new ActiveXObject('Microsoft.XMLHTTP')
}
xhr.onreadystatechange = function() {
if(options.dataType === 'json'){
if (xhr.readyState == 4) {
window.clearTimeout(that.timeoutFlag);
var status = xhr.status;
if (status >= 200 && status < 300) {
// 如果需要像 html 表单那样 POST 数据,请使用 setRequestHeader() 来添加 http 头。
options.success && options.success(xhr.responseText, xhr.responseXML);
} else {
options.error && options.error(status);
}
}
} else {
if (xmlHttp.readyState == 4 && xmlHttp.status == 200) {
window.clearTimeout(that.timeoutFlag);
var oScript = document.createElement('script');
document.body.appendChild(oScript);
var callbackname = 'ajaxCallBack'
oScript.src = options.url + "?" + params+'&callback='+callbackname;
window['ajaxCallBack'] = function(data) {
options.success(data);
document.body.removeChild(oScript);
};
}
}
};
if (options.type == 'GET') {
xhr.open("GET", options.url + '?' + params, options.async);
xhr.send(null)
} else if (options.type == 'POST') {
xhr.open('POST', options.url, options.async);
if(options.contentType=="undefined"||options.contentType==null){
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send(params);
}else{
xhr.setRequestHeader('Content-Type', options.contentType);
xhr.send(JSON.stringify(options.data));
}
}
this.timeoutFlag=window.setTimeout(function(){//计时器,超时后处理
window.clearTimeout(that.timeoutFlag);
//options.error("timeout");
xhr.abort();
}.bind(this),options.timeout);
}
function getParams(data) {
var arr = [];
for (var param in data) {
arr.push(encodeURIComponent(param) + '=' + encodeURIComponent(data[param]));
}
return arr.join('&');
}
封装代码的使用:
ajax({
url: "", //请求地址
type: 'GET', //请求方式
async:true,//同步异步设置
timeout:6000,//超时设置
data: {
name: '',
age: '',
email: ''
}, //请求参数
success: function(response, xml) {
console.log(response); // 此处执行请求成功后的回调
},
error: function(status) {
console.log('状态码为' + status); // 此处为请求失败后的回调
}
})
为了方便记忆和使用,像上面封装原生JS的ajax的属性和方法在jQuery中的$.ajax也都有。
在MVC页面中,多个PartialView(分部视图)同时加载,一般使用异步请求。一个原因是分部视图加载顺序不分先后,另一个原因是即使有一个分部视图加载失败也不会影响其他视图的加载。一般只有两种情况下会使用到同步请求:1.需要控制ajax请求返回结果顺序时;2.在ajax请求时需锁住浏览器时。
5.用promise手写实现AJAX
function getJSON(url) {
// 创建一个 promise 对象
let promise = new Promise(function(resolve, reject) {
let xhr = new XMLHttpRequest();
// 新建一个 http 请求
xhr.open("GET", url, true);
// 设置状态的监听函数
xhr.onreadystatechange = function() {
if (this.readyState !== 4) return;
// 当请求成功或失败时,改变 promise 的状态
if (this.status === 200) {
resolve(this.response);
} else {
reject(new Error(this.statusText));
}
};
// 设置错误监听函数
xhr.onerror = function() {
reject(new Error(this.statusText));
};
// 设置响应的数据类型
xhr.responseType = "json";
// 设置请求头信息
xhr.setRequestHeader("Accept", "application/json");
// 发送 http 请求
xhr.send(null);
});
return promise;
}