Ajax 入门

1. 定义

Ajax 是 Asynchronous JavaScript and XML 的首字母缩写,翻译成中文是”异步 JavaScript 和 XML”。是由大神 Jesse James Garrett 在一篇题为 Ajax: A new Approach to Web Applications的在线文章中提出来的。

这一技术能够在无需刷新页面的情况下,在后台异步地向服务器请求额外的数据,实现更新页面部分内容的目的。传统的网页如果要更新内容必须要刷新整个页面,而且采用的是 “操作,等待,在操作” 的交互模式。

Ajax 并不是一门新的编程语言,它是现有的多种技术的组合。主要包括:

  1. 浏览器内置的 XMLHttpRequest 对象
  2. JavaScript 和 HTML DOM

一个典型的应用就是 Google 搜索框,当你在搜索框中输入字符时, JavaScript 会将你输入的字符传到服务器端,服务器返回一个搜索建议的列表。

2. 实现原理

在进入具体细节之间,我们首先来看一下这门技术的实现原理:

  1. 页面中一个事件处理程序被触发
  2. 通过 JavaScript 创建一个 XMLHttpRequest 对象
  3. 浏览器通过 XMLHttpRequest 对象向服务器发送一个请求
  4. 服务器处理请求并将结果通过 XMLHttpRequest 对象返回给浏览器
  5. 浏览器提取新数据,利用 DOM 将其插入到页面中

从上面我们不难看出,这门技术实现的第一步就是为页面中某一个节点添加事件处理程序。通过这个事件的触发,来引发后面的一系列操作。这也是 Ajax 为什么能够实现动态交互的原因。

3. XMLHttpRequest 对象

Ajax 技术的核心就是 XMLHttpRequest 对象,浏览器和服务器都通过该对象发送和接收数据。

3.1 创建 XMLHttpRequest 对象

IE7+、 Firefox、 Opera、 Chrome 和 Safari 都支持原生的 XHR 对象,所以,如果不考虑 IE7 之前的版本,大可只用原生的 XHR 对象,如下:

var request = new XMLHttpRequest();

但是如果要考虑到 IE7 之前的版本,那么就要做兼容性的处理:

function createRequest() {
try {
request = new XMLHttpRequest();
} catch(tryMS) {
try {
request = new ActiveXObject("Microsoft.XMLHTTP");
} catch(failed) {
request = null;
}
}
return request;
}
var request = createRequest();

3.2 XMLHttpRequest 对象的用法

3.2.1 初始化(启动)及发送

在使用 XHR 对象时,首先必须调用 open 方法将其初始化,它接受三个参数:要发送的请求类型(get、post),接收请求的 URL 和表示是否异步的布尔值。

request.open("get", "test.php", true);

open 方法并不会真正发送请求,而只是启动一个请求以备启动。再调用 send 方法即可发送请求。 send 方法接受一个参数,表示作为请求主体的数据。如果请求类型为 get,那么就必须填 null。

request.send(null);

当请求发送到服务器端,响应的数据会自动填充 XHR 对象的属性。一共有四个属性:

属性名 说明
status 响应的 HTTP 状态码
statusText 响应的 HTTP 状态说明
responseText 作为响应主体被返回的文本
responseXML 如果响应主体内容类型不是文本,而是”text/xml”或者”application/xml”,则返回包含响应数据的 XML DOM 文档

浏览器接收到响应之后,首先需要检查 status 属性的值,以确定响应已经成功返回。一般以 HTTP 状态码为 200 作为成功的标志。此外,状态码为 304 表示请求的资源并没有被修改,可以直接使用浏览器缓存中的版本,也意味着响应是有效的。

3.2.2 获取、设置头信息

  1. 使用 setRequestHeader() 设置单个请求头信息

    request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
  2. 使用 getResponseHeader() 获取单个响应头信息

    request.getResponseHeader("Content-Type");
  3. 使用 getAllResponseHeaders() 获取所有响应头信息

    request.getAllResponseHeaders();

3.2.3 同步

someNode.onclick = function() {
var request = new createRequest();
request.open("get", "test.php", false);
request.send(null);
// 根据返回的状态码进行处理
if (request.status === 200 || request.status === 304) {
alert(request.responseText);
} else {
alert("status: " + request.status + " statusText: " + request.statusText);
}
}

3.2.4 异步

上面是同步的方法,异步才是我们真正常用的手段。使用异步时,需要触发 readystatechange 事件,然后检测 XHR 对象的 readyState 属性,接着再进行状态码的判断。 readyState 属性表示请求、响应过程的当前活动阶段。有五个值:

状态 说明
0 未初始化 尚未调用 open() 方法
1 初始化完成,启动 已经调用 open() 方法,但尚未调用 send() 方法
2 发送 已经调用 send() 方法,但尚未接收到响应
3 接收信息 已经接受到部分响应数据
4 完成接收 已经接收到全部响应数据,可以在客户端使用了
someNode.onclick = function() {
var request = new createRequest();
request.onreadystatechange = function() {
if (request.readyState === 4) {
if (request.status === 200 || request.status === 304) {
alert(request.responseText);
} else {
alert("status: " + request.status + " statusText: " + request.statusText);
}
}
};
request.open("get", "test.php", false);
request.send(null);
}

4. GET or POST ?

4.1 GET 请求

GET 是最常见的请求类型,用于向服务器查询某些信息。 必要时,可以将查询字符串参数添加到 URL 的末尾,以便将信息发送给服务器。 对于 XHR,位于传入 open() 方法的 URL 末尾的字符串必须经过正确的编码才行。查询字符串的每个参数的名称和值都要使用 encodeURIComponent() 进行编码,然后才能放到 URL 末尾。而且,所有的名值对都必须用和号(&)分隔。 下面这个函数可以向现有 URL 的末尾添加查询字符串参数:

function addURLParam(url, name, value) {
url += (url.indexOf("?") === -1 ? "?" : "&");
url += encodeURIComponent(name) + "=" + encodeURIComponent(value);
return url;
}

下面是使用这个函数来构建请求 URL 的示例:

var url = "test.php";
url = addURLParam(url, "name", "Paco");
url = addURLParam(url, "age", "26");
request.open("get", url, true);

4.2 POST 请求

使用频率仅次于 GET,通常用于向服务器发送应该被保存的数据。 POST 请求应该把数据作为请求的主体提交。POST 请求的主体可以包含非常多的数据,而且格式不限。 发送 POST 请求的第一步是将 open() 方法的第一个参数设置为 “post”;第二步是向 send() 方法传入数据。

function addRequestData(requestData, name, value) {
if (requestData !== "") {
requestData = requestData + "&";
}
requestData += encodeURIComponent(name) + "=" + encodeURIComponent(value);
return requestData;
}
var requestData = "";
requestData = addRequestData(requestData, "name", "Paco");
requestData = addRequestData(requestData, "age", "26");
request.open("post", "test.php", true);
request.send(requestData);

5. 封装 Ajax

首先我们要列举一下需要传入该函数的参数:

  • url:需要请求资源的地址
  • type:请求所采取的方法,get 还是 post
  • data:所要传输的数据,一个键值对象
  • onsuccess:请求成功时调用的函数
  • onfial:请求失败时调用的函数

我们可以将所有的这些参数,以键值对的形式封装成一个对象:

var options = {
url:
type:
data:
onsuccess:
onfail:
}

将这个对象最为参数传入到我们封装的 Ajax 函数中,实现如下:

function ajax(options) {
// 1.创建 XHR 对象
try {
xhr = new XMLHttpRequest();
} catch(tryMS) {
try {
xhr = new ActiveXObject("Microsoft.XMLHTTP");
} catch(failed) {
xhr = full;
}
}
// 2. 序列化请求参数
var param = "";
var data = options.data ? options.data : -1;
if (typeof data === "object") {
for (var key in data) {
if (data.hasOwnProperty(key)) {
param += encodeURIComponent(key) + "=" + encodeURIComponent(data.key) + "&";
}
}
param.replace(/&$/, "");
}
// 3. 启动并发送请求
var type = options.type ? options.type.toUpperCase() : "GET";
if (options.type === "GET") {
xhr.open(type, options.url + "?" + param, true);
xhr.send();
} else {
xhr.open(type, options.url, true);
xhr.setRequestHeader("Content-Type", "application/x-www.form-urlencoded");
xhr.send(param);
}
// 4. 接收响应
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
options.onsuccess(xhr.responseText);
} else {
if (options.onfail) {
options.onfail(xhr);
}
}
}
}
// 5. 返回请求对象
return xhr;
}