Ajax 编程
本文主要包含以下内容:
- Ajax 出现之前的世界
- Ajax 基本介绍
- Ajax 具体编码
- 封装通用的 Ajax 方法
Ajax 出现之前的世界
在前面的文章介绍中,我已经为大家介绍了 HTTP 协议,我们的网络就是基于 HTTP 协议来进行通信的。可以说,因为网络的出现,改变了我们这个世界,通过网络,我们能够和世界各地的人实时的分享信息。
但是早期的网络通信,有一个最大的特点,就是每次请求都是一个完整的页面,所以在那个年代也不存在“ Web 应用”这个词,更多的是叫做“网站”,而“网站”的特点就是由多个页面组成的。
从上图可以看出,页面与页面之间的跳转全靠 a 标签,每当用户点击一个 a 标签时,浏览器就会向服务器请求新的 HTML 页面。
客户端和服务器端唯一的交互就是通过 Form 表单,但是 Form 表单也不例外,还记得 Form 标签中的 action 属性么?那就是用来指定用户向服务器提交了信息后跳转到哪一个页面的,之后要跳转的那个页面也毫无意外是从服务器端返回的一个完整 HTML 页面。
在那个网速不佳的年代,重新刷新页面的成本是很高的,通常会导致在加载页面时屏幕变空白。
明白了 Ajax 出现之前的互联网形态后,你明显会感觉到那个时候太过原始,甚至还有点幼稚,所以那个时候的电商网站,也绝不会有购物车这个功能,基本就是看上某个商品后,提交购买商品信息表单然后就结束了整个购买流程。
和我们现在的互联网形态,显然有着天壤之别。
可以毫不夸张的讲,Ajax 的出现,为我们的互联网带来了一次全新的革命,如果将 Ajax 之前的互联网形态称之为 1.0 的话,那么 Ajax 的出现就是将互联网形态升级到了 2.0。
Ajax 基本介绍
1999 年,微软公司在自家旗下的 IE5 中实现了一个叫 XMLHttp ActiveX 的控件。它最初是为 outlook Web 客户端而开发的,允许在后台使用 JavaScript 异步发送数据。随后其他浏览器也实现了这种技术,不过它仍然是一个相对未知的功能,很少使用。
2004 年和 2005 年,Google 公司相继推出了 Gmail 和 Google 地图。
Gmail
Google 地图
从这个时候开始,异步加载技术开始备受关注。这些 Web 应用程序使用的异步加载技术,通过更改页面部分内容而不进行全面刷新来增强了用户的体验。让用户感觉更像是一个桌面应用程序。
2005 年,Jesse James Garrett 发布了一篇文章,叫做“ Ajax : 一种新的 Web 应用程序方法”。
在文章中,Jesse James Garrett 提到了 Google 在其最近的 Web 应用程序中使用到的技术,而 Ajax 是实际上是几个单词的首字母缩写词,指的是该技术在使用的过程中所涉及到的不同部分,英语全称为 Asynchronous JavaScript and XML。
-
Asynchronous:翻译成中文是异步的意思,当发送数据请求时,程序不必停下来等待响应。它可以继续运行,等待响应收到时触发事件。通过使用回调来管理这种过程,程序能够以有效的方式运行,避免了数据来回传输带来的延迟。
-
JavaScript:利用 JavaScript 我们可以接收来自服务器端返回的数据,并将这些数据通过 DOM 接口实时的更新到页面上。
-
XML:最开始术语 Ajax 被创造时,经常用 XML 文档来返回数据。但是实际上可以发送许多不同类型的数据。到目前为止,在 Ajax 中最常用的是 JSON,它比 XML 更轻量且更易于解析。JSON 还具备被 JavaScript 原生支持的优点,所以我们可以处理 JavaScript 对象,而不必使用 DOM 方法来解析 XML 文件。
在 Garrett 的文章发表以后,Ajax 的使用真正开始起飞。现在用户不必刷新页面,就可以在网页上看到新的内容。
常见的场景:
-
Google、百度地图
-
购物车
-
搜索联想
总之,只要客户端和服务器端发生了网络通信,但是页面并没有刷新的场景,基本上就是使用 Ajax 来实现的。
Ajax 实现了与服务器的异步通信、局部刷新页面,这是 Ajax 技术的核心所在。当然 Ajax 也不是只有优点没有缺点,关于 Ajax 的优缺点总结如下:
优点
-
页面无刷新,在页面内与服务器通信,减少用户等待的时间,增强了用户体验。
-
使用异步方式与服务器通信,响应速度快。
-
可以把一些原本服务器的工作转接到客户端,利用客户端闲置的能力来处理,减轻了服务器和宽带的负担,节约空间和宽带租用成本。
-
基于标准化的并被广泛支持的技术,不需要下载插件或者小程序。
缺点
-
无法进行操作的后退,即不支持浏览器的页面后退。
-
对搜索引擎的支持比较弱。
-
可能会影响程序中的异常处理机制。
-
安全问题。对一些网站的攻击,如 CSRF、XXS、SQL 注入等不能很好的防御。
Ajax 具体编码
了解了什么是 Ajax 后,接下来我们就要进入到实际的编码环节了。
使用原生的 Ajax 大致包括以下 4 个步骤:
-
创建 XMLHttpRequest 对象
-
发出 HTTP 请求
-
接收服务器传回的数据
-
更新网页数据
接下来我们来具体看一下每一个部分。
1. 创建 XMLHttpRequest 对象
创建一个 XMLHttpRequest 对象,也叫实例化一个 XMLHttpRequest 对象。因为 XMLHttpRequest( ) 本身是一个构造函数。
var xhr = new XMLHttpRequest();
IE5 是第一款引入 XMLHttpRequest 对象的浏览器。在 IE5 和 IE6 中,XHR 对象是通过 MSXML 库中的一个 ActiveX 对象实现的。
var xhr = new ActiveXObject('Microsoft.XMLHTTP');
而 IE7+ 及其他标准浏览器都支持原生的 XMLHttpRequest 对象。为了应对所有浏览器,下面是创建 XMLHttpRequest 对象的兼容写法。
var xhr;
if(window.XMLHttpRequest){
// IE7+, Firefox, Chrome, Opera, Safari 浏览器执行代码
xhr = new XMLHttpRequest();
} else {
// IE6, IE5 浏览器执行代码
xhr = new ActiveXObject( 'Microsoft.XMLHTTP' );
}
2. 打开连接
XMLHttpRequest 的实例对象提供了一个 open( ) 方法用来打开客户端与服务端之间的连接。该方法规定了请求的类型、请求的路径以及是否异步处理请求。语法结构如下:
xhr.open(method, url, async);
参数说明:
-
method:发送请求的方式,一般采用大写,例如 GET、POST。
-
url:请求资源的位置路径。
-
async:控制是否异步处理请求。取值有 true(异步)和 false(同步),默认为 true。一般来讲,都会产用异步的方式来发送请求,不然 Ajax 将变得毫无意义。
3. 发送请求
XMLHttpRequest 的实例对象提供了一个 send( ) 方法用于客户端向服务端发送请求。
xhr.send();
请求参数
在向服务端发送请求的同时,是可以传递数据至服务端的。而请求方式的不同,数据的传递方式也不同。
GET 请求:传递的数据是跟在 open( ) 方法中的 url 后面。
xhr.open("GET","/users/isUser?username=zhangsan&pwd=123");
xhr.send(null);
POST 请求:传递的数据是放在 send( ) 方法的参数中。调用 send( ) 方法之前需要设定 Content-Type 头信息,模拟 HTTP 的 POST 方法发送一个表单,这样服务器才会知道如何处理上传的内容。
参数的提交格式和 GET 方法中 url 的写法一样。设置头信息前必须先调用 open( ) 方法。
xhr.open("POST","login.php",true);
xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded;charset=UTF-8");
xhr.send("username=zhangsan&pwd=123");
和 POST 相比,GET 的请求方式更简单也更快,并且在大部分情况下都能使用。但是,在以下情况中,请使用 POST 请求:
- 无法使用缓存文件(更新服务器上的文件或者数据库)
- 向服务器发送大量数据(POST 没有数据量的限制)
- 发送包含未知字符的用户输入时,POST 比 GET 更稳定也更可靠
4. 处理服务器返回的消息
一个完整的 HTTP 响应由状态码、响应头集合和响应主体组成。在收到响应后,可以通过 XHR 对象的属性来获取响应内容。
常用的属性有以下 4 个:
-
responseText: 作为响应主体被返回的文本(文本形式)
-
responseXML: 如果响应的内容类型是 text/xml 或 application/xml,这个属性中将保存着响应数据的 XML DOM 文档( document 形式)
-
status: HTTP 状态码(数字形式)
-
statusText: HTTP 状态说明(文本形式)
另外,第 2 步打开连接时,选择处理请求的方式不同,第 4 步处理服务器返回消息的方式也有所不同。
同步处理请求
当处理请求的方式为同步时,直接使用 XMLHttpRequest 的实例对象的 responseText 属性用来接收服务端返回的消息。
xhr.responseText;
但是不推荐使用同步处理请求的方式。使用这种方式会导致 JavaScript 代码要一直等到服务器响应就绪后才继续执行,如果服务器繁忙或缓慢,应用程序就会挂起或停止。但是对于一些小型的请求,也是可以的。
异步处理请求
当处理请求的方式为异步时,需要通过 XMLHttpRequest 的实例对象提供的 onreadystatechange 事件来监听并处理服务器返回的消息。
xhr.onreadystatechange = function(){
// ...
}
从“请求准备发送”到最后“请求发送成功”,中间执行了一系列的任务,分别为:
-
请求未初始化(0):即还没有调用 send( ) 方法;
-
服务器连接已建立(1):即已调用 send( ) 方法,正在发送请求;
-
请求已接收(2):即 send( ) 方法执行完成;
-
请求处理中(3):即正在解析响应内容;
-
请求已完成(4):且响应已就绪,即响应内容解析完成,可以在客户端进行调用了;
每执行一个任务,XMLHttpRequest 对象的状态值 readyState 都会发生改变。只要 readyState 属性发生改变时,就会触发 onreadystatechange 事件。
按照任务顺序,状态值 readyState 依次从 0 到 4 发生改变。
只有当状态值为 4 时,才表示请求完成。请求完成后,判断请求状态,状态码 status 为 200 时表示请求成功。只有请求完成并且成功了,才能处理服务端返回的消息。
xhr.onreadystatechange = function(){
if(xhr.readyState == 4 && xhr.status == 200){
var text = xhr.responseText;
console.log( text );
}
}
5. 完整代码
我们将上面分解的每一步整合在一起,就是一个完整的 Ajax 请求代码了。
// 1. 创建 XHR 对象
var xhr;
if (window.XMLHttpRequest) {
// IE7+, Firefox, Chrome, Opera, Safari 浏览器执行代码
xhr = new XMLHttpRequest();
} else {
// IE6, IE5 浏览器执行代码
xhr = new ActiveXObject('Microsoft.XMLHTTP');
}
// 2. 打开连接
xhr.open("get", "/users/isPhoneExist?phone=12313212311"); // 第三个参数默认 true,表示异步;get 请求的参数跟在路径后面。
// 3. 发送请求
xhr.send(); // send 方法默认没有参数。
//xhr.send("phone=123&pwd=123"); // 请求是 post,且想要传参,send 方法才有参数。
// 4. 通过事件监听并处理服务器返回的消息
xhr.onreadystatechange = function () {
// 服务器正常的响应数据回来
if (xhr.readyState == 4 && xhr.status == 200) {
var text = xhr.responseText; // 得到的 text 是字符串
DOMObject.innerHTML = text; // 处理响应数据
}
}
封装通用的 Ajax 方法
通过上面的学习,我们已经掌握了如何创建一个 XHR 对象,并使用该对象来发送一个 Ajax 请求和服务器端进行通信。
但是我们也明显能感受到整个过程显得比较繁琐,里面还包含了一些和业务逻辑无关的兼容性的代码在里面,所以我们尝试来将这个 Ajax 请求进行一个方法的封装,封装完毕后,用户调用该方法传入必要的信息即可,无需再做兼容性的处理。
具体的封装示例如下:
// 辅助函数:该函数的功能就是将对象转为字符串
// {username:'zhangsan',age:18} ====> name=zhangsan&age=18
function toData(obj) {
if (obj === null) {
return obj
}
let arr = [];
for (let i in obj) {
arr.push(`${i}=${obj[i]}`); // ['name=zhangsan','age=18']
}
return arr.join('&');
}
// 封装的 ajax 函数 用于发送 ajax 请求
function ajax(obj) {
// 确定提交的请求的方式
obj.type = obj.type || 'get'
// 确定提交的方式是同步还是异步
obj.async = obj.async || true;
// 设置提交数据的默认值
obj.data = obj.data || null;
let xhr = null; // 跑腿的人
// 1. 创建 xhr 对象
if (window.XMLHttpRequest) {
// 非 IE 浏览器
xhr = new XMLHttpRequest();
} else {
// 是 IE 浏览器
xhr = new ActiveXObject('Microsoft.XMLHTTP');
}
// 2. 请求对应的服务器地址(open) 如果有数据 向服务器发送数据(send)
if (obj.type === 'get') {
// 说明是以 get 形式来发送请求
let url = obj.url + '?' + toData(obj.data) // 重构 url 将数据附加在 url 后面 test.php?name=zhangsan&pwd=123456
xhr.open('get', url, obj.async);
xhr.send(null);
} else {
// 说明是以 post 形式来发送请求
xhr.open('post', obj.url, obj.async);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8");
xhr.send(toData(obj.data));
}
// 3. 进行状态的监听 将从服务器取回来的数据返回
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
// 说明 xhr 已经从服务器带着数据回来了
obj.success(xhr.responseText);
}
}
}
在上面的代码中,我们封装了两个函数,一个是 ajax 函数,另一个是辅助函数 toData。
辅助函数 toData 的作用是将将对象转为字符串,例如用户传入的是 {username:‘zhangsan’,userage:18},通过该函数可以转换为 name=zhangsan&age=18。
而 ajax 函数就是我们具体发送 Ajax 请求的函数,里面封装了创建 XHR 对象的兼容性操作,以及根据用户不同的请求方式进行不同的处理操作。
通用的 Ajax 函数封装完毕后,之后我们要发送 Ajax 请求,整个过程就显得轻松多了,例如:
ajax({
url : 'getStudent',
type : 'get',
success : function(data){
render(JSON.parse(data).list);
}
});
总结
-
Ajax 并不是一门崭新的技术,而是当时所存在的几种技术的结合。
-
使用原生 Ajax 的步骤大致可以分为:
- 创建 XMLHttpRequest 对象,
- 发出 HTTP 请求
- 接收服务器传回的数据
- 更新网页的数据
-
根据浏览器的不同,创建 XMLHttpRequest 对象的方式也不同。
-
发送请求时要用到 open 方法和 send 方法。
-
readyState 属性有 5 种状态值,一般我们只对值为 4 的时候采取相应的操作。
-EOF-