Ajax 的执行流程以及原生 JS 封装一个 Ajax
1.引言
Ajax(Asynchronous JavaScript and XML)即异步的 JavaScript 与 XML,它不是一种语言,它是一组需要一起协同工作的技术。另外虽然名字中有 XML,但是 Ajax 通信与数据格式无关,实际上我们通常用 JSON 格式的数据来代替。
传统的 Web 应用允许用户端填写表单(form),当提交表单时就向网页服务器发送一个请求。服务器接收并处理传来的表单,然后送回一个新的网页,但这个做法浪费了许多带宽,因为在前后两个页面中的大部分 HTML 码往往是 相同 的。由于每次应用的沟通都需要向服务器发送请求,应用的回应时间依赖于服务器的回应时间。这导致了用户界面的回应比本机应用慢得多。
Ajax 技术的核心是 XMLHttpRequest 对象 (简称 XHR),XHR 为向服务器发送请求和解析服务器相应提供了流畅的接口。能够以异步的方式从服务器获取更多的信息,也就意味着用户单击后,可以 不用刷新整个页面也能获取到新数据 。也就是说,可以使用 XHR 对象获取新的数据,然后再通过 DOM 将数据插入到页面中。
2.Ajax 工作流程
在这里我不考虑 IE7 之前的版本,有兴趣的同学可以自己去看 JS 高程上对 IE7 之前兼容性的实现。
S1. 怎样发送 http 请求
通过 XMLHttpRequest 构造函数就可以在浏览器中创建一个 XHR 对象,通过该对象就可以向服务器发送一个 http 请求。
var xhr = new XMLHttpRequest();
当我们发送完一个请求后,会收到响应,我们要告诉 XHR 对象由哪一个 JavaScript 函数处理响应。其中,XMLHttpRequest.onreadystatechange 会在 XMLHttpRequest 的 readyState 属性发生改变时触发相关事件函数,函数既可以是命名的。
xhr.onreadystatechange = nameOfTheFunction;
需要注意的是函数名后没有参数,因为我们只是把一个引用赋值给了函数,而不是真正的调用了它。
当然我们也可以通过匿名函数处理相关响应:
xhr.onreadystatechange = function() {
// 此处处理服务器的响应
};
接下来我们需要调用 XHR 对象的 open()和 send()方法分别对应着初始化和发送一个请求。
xhr.open("GET", "http://www.example.org/example.txt", true);
xhr.send();
其中 open()方法 并不会 真正发送请求,而只是启动一个请求以备发送。 XMLHttpRequest.open()方法接受三个参数:
xhr.open(method, url, async);
-
method 要使用的 HTTP 方法,比如「GET」、「POST」、「PUT」、「DELETE」、等,一定要保证这些方法是大写字母,否则其他一些浏览器(比如 FireFox)可能无法处理这个请求。
-
url 一个表示要向其发送请求的 URL。由于安全原因(同源策略),默认不能调用第三方 URL 域名,即不能跨域请求(如果非要跨域请求也是有办法的,后续我会写一篇关于跨域的。)
-
async 一个可选的布尔参数,默认为 true,表示要不要异步执行操作。如果值为 false,则代表同步请求,send()方法直到前面响应到达后继续执行,通俗话说就是在那干等。如果为 true(默认),则为异步操作,会在请求发送后立即执行。
XMLHttpRequest.send() 方法接受一个可选的参数,将其作为请求主体;如果请求方法是 GET 或者 HEAD,则应将请求主体设置为 null。如果是 POST 请求的话。发送表单数据时应该用服务器可以解析的格式,像查询语句,或者其他格式, 类似 multipart/form-data,JSON,XML 等。
还有非常重要的一点是,如果使用 POST 方法,应该在调用 send()方法之前使用 setRequestHeader() 方法设置 Content-Type 头部来指定数据流的 MIME 类型。曾经掉进这个坑…
假如我们调用 send() 方法获取表单数据那么应该这样写:
xhr.open("GET", "http://www.example.org/example.txt", true);
// 发送合适的请求头信息
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xhr.send();
S2. 处理服务器响应
假设我们发送的是异步请求,JavaScript 会继续执行,而不用等待响应。我们可以检测 XHR 对象的 readyState 属性,该属性表示请求/响应过程的当前活动状态,这些状态值如下图所示:
通常我们对 readyState 值为4的阶段感兴趣,因为此时所有的数据已经就绪。
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
// Everything is good, the response was received.
} else {
// Not ready yet.
}
};
接下来我们要检测 HTTP 响应的 response code 来区别对待成功或者不成功的 Ajax 调用。
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
// Perfect!
}
} else {
// There was a problem with the request.
}
状态码 304 代表着请求的资源并没有被修改,可以直接使用浏览器缓存的版本,所以意味着响应也是有效的。
当我们检查完请求状态和 HTTP 响应码后,我们就可以用服务器返回的数据做接下来的事情了:
-
xhr.responseText -作为响应主体被返回的文本
-
httpRequest.responseXML -以 XMLDocument 对象方式返回,之后就可以使用 JavaScript 来处理
S3 一个简单的例子(以 GET 为例)
用户点击页面上的 “Make a request” 按钮,然后以 Ajax 方式 alert() test.html 文件内容。
<button id="ajaxButton" type="button">Make a request</button>
<script>
(function() {
// 监听按钮点击事件
document.getElementById("ajaxButton").addEventListener('click', makeRequest);
function makeRequest() {
// 创建xhr对象
var xhr = new XMLHttpRequest();
if (!xhr) {
alert('Giving up :( Cannot create an XMLHTTP instance');
return false;
}
xhr.onreadystatechange = alertContents;
xhr.open('GET', 'test.html');
xhr.send(null);
}
function alertContents() {
try {
// 检测readyState状态
if (xhr.readyState === 4) {
// 检测HTTP响应码
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
alert(xhr.responseText);
} else {
alert('There was a problem with the request.');
}
}
}
catch( e ) {
alert('Caught Exception: ' + e.description);
}
}
})();
</script>
3.手动封装
function Ajax(obj) {
this.type = obj.type || "";
this.url = obj.url || "";
this.callback = obj.callback || "";
this.data = obj.data || "";
}
Ajax.prototype.send = function(type, url, callback, data) {
var type = type || this.type;
var url = url || this.url;
var callback = callback || this.callback;
var data = data || this.data;
// 创建XHR对象
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
// 监听状态
if (xhr.readyState === 4) {
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
// 解析JSON字符串给回调函数
callback(JSON.parse(xhr.responseText));
} else {
console.log("error");
return false;
}
}
};
// GET方法
if (type.toUpperCase() === "GET") {
if (typeof data === "object") {
var data_send = "?";
// 将data拼接到url中
for (const key in data) {
data_send += key + "=" + data[key];
data_send += "&";
}
xhr.open(type, url + data_send, true);
xhr.send(null);
}
// POST方法
} else if (type.toUpperCase() === "POST") {
xhr.open(type, url, true);
// 说明Content-Type
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhr.send(JSON.stringify(data));
} else {
console.log("error");
return false;
}
};
示例用法
var ajax = new Ajax({
method: "get", //设置ajax方法
url: "http://www.example.com",
callback: function(res) {
//设置回调函数
alert(res);
},
data: data //需要传递的数据
});
ajax.send();
参考文献
-
《JavaScript 高级程序设计》