在Ajax出现之前,网页页面加载后,需要请求服务端数据刷新页面时,整个页面都需要重新加载一次,对于只有一小个区域的数据更新也必须刷新整个页面。

这个窘境在出现XMLHttpRequest对象之后得到了改善,而后人们统称不刷新整个页面的情况下请求服务端数据的技术为“Ajax”,即“Asynchromous JavaScript + XML”的简称。

XMLHttpRequest对象

Ajax的核心是XMLHttpRequest对象,简称“XHR”,熟悉XHR对象是Web开发者必须掌握的技能。

在IE7以上的浏览器及其它流行浏览器下,XMLHttpRequest对象都是原生进行支持的,如果抛开老浏览器之外,创建XHR对象的代码如下:

var xhr = new XMLHttpRequest();

用法

在创建了XHR对象之后,第一个要调用的方法就是open方法,该方法接收3个参数:

  1. 请求类型:“get”或“post”等;
  2. 请求的地址;
  3. 是否一部发送请求:true表示异步请求,false表示同步请求;

调用open方法后并不会真正的发送请求,而是启动了一个请求等待发送,这里要注意一点:只能向同一个域中使用相同端口和协议的URL发送请求,如果有任一一个不一致,会导致安全错误。关于跨域的问题可以详见下一篇笔记。

发送请求调用send方法,该方法接受一个参数,即请求要发送的数据,如果没有数据,请传入null,这是为了兼容多个浏览器的做法。

当请求返回并得到服务端的数据后,会自动填充到XHR对象的属性里:

  • responseText:返回的文本数据;
  • responseXML:如果数据为XML文档,则这里保存对应的XML DOM对象;
  • status:响应的HTTP状态码;
  • statusText:HTTP状态说明。

对于是否得到正确的返回,可以通过检查状态码来实现,status为200时,表示响应成功;status为304时表示资源没有修改,直接使用缓存中的数据。更多地状态码点击这里。

 if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {
// 请求成功
} else {
// 请求出错
}

由于状态码[200-299]这个区间都是表示成功,所以不能只判断等于200或304。

同步请求

如果使用同步请求,则需要等待服务端返回消息后,后面的JS脚本才会继续执行。

 // 创建同步连接
var xhr = new XMLHttpRequest();
xhr.open("get", "example.php", false);
xhr.send(null); // 判断返回
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {
console.log(xhr.responseText);
} else {
console.log("request error: " + xhr.status);
} // 等待请求返回后才继续执行
console.log("run code!");

当然大部分的情况下,我们还是希望可以异步执行,不阻塞页面主线程。

异步请求

使用异步请求,需要监听XHR对象的readystatechange事件,在XHR对象的一次请求中,不同阶段会有不同的readyState码:

  • 0:未初始化,未调用open方法。
  • 1:启动,未调用send方法。
  • 2:发送,未收到响应。
  • 3:接收,收到部分响应。
  • 4:完成,收到全部响应。

每次readyState值改变时都会抛出一个readystatechange事件,我们监听该事件即可,如下:

 // 创建异步连接
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {
console.log(xhr.responseText);
} else {
console.log("request error: " + xhr.status);
}
}
}
xhr.open("get", "example.php", true);
xhr.send(null); // 不用等待请求返回,立即执行
console.log("run code!");

需要注意的是,这里没有向监听函数传递event对象。同时该事件也不支持DOM2级的事件方法。

取消请求

在异步请求时,可以通过调用abort方法来中断本次请求。

HTTP头部信息

每个HTTP的请求和返回都会带有相应的头部信息,头部信息用来记录和本次HTTP请求和返回数据之外的额外数据,这些数据一般用在系统层面,XHR提供了操作这两种头部信息的方法。

下面列出请求时的默认头部信息:

  • Accept:浏览器能够处理的内容类型;
  • Accept-Charset:浏览器能够显示的字符集;
  • Accept-Encoding:浏览器能够处理的压缩编码;
  • Accept-Language:浏览器当前的语言;
  • Connection:与服务端的连接类型;
  • Cookie:当前页面设置的任何Cookie;
  • Host:发送的请求所在的域;
  • Referer:发送的请求的URI;
  • User-Agent:浏览器的用户代理字符串。

设置发送的头部信息

在send方法调用之前可以使用setRequestHeader方法添加自定义的请求头部信息。

xhr.setRequestHeader("myHeader", "myValue");

自定义的头部信息名称注意不要和系统定义的一致,如果覆盖系统头部信息则有可能会导致问题。

获取返回的头部信息

XHR返回消息之后,可以通过getResponseHeader方法传人头部字段名称得到对应的值,或者通过getAllResponseHeaders方法得到所有头部信息的多行文本内容。

Get方法

通过向请求的地址添加“?name1=value1&name2=value2”之类的键值对即可向Get类型的请求添加参数,不过要注意的是,name和value都需要使用encodeURIComponent方法进行编码,否则会出现格式不正确的问题,下面提供一个通用方法:

 function addURLParam(url, name, value) {
url += (url.indexOf("?") == -1 ? "?" : "&");
url += encodeURIComponent(name) + "=" + encodeURIComponent(value);
return url;
} var url = "example.php";
url = addURLParam(url, "name", "Li Lei");
url = addURLParam(url, "age", 28);
console.log(url); // example.php?name=Li%20Lei&age=28

可以适当封装之后用到项目中。

Post方法

和Get通过直接将参数放到URL中的做法不同,Post请求可以传递大量的数据。

Post请求发送的数据作为参数传递给send方法即可,但是要注意,send方法仅接收字符串和XML DOM对象,且格式不限,可以根据需要定义自己的格式,在PHP中,使用$HTTP_RAW_POST_DATA获取。

我们知道,在表单中,我们通过POST方式发送的数据在PHP后端可以通过$_POST的方式获取,且和GET方法获取方式一致。

如果我们希望我们发送的数据可以和表单数据一样可以在后端通过$_POST的方式获取的话,可以用下面的方法:

 var content = [];

 function addURLParam(name, value) {
content.push(encodeURIComponent(name) + "=" + encodeURIComponent(value));
} addURLParam(url, "name", "Li Lei");
addURLParam(url, "age", 28); var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {
console.log(xhr.responseText);
} else {
console.log("request error: " + xhr.status);
}
}
}
xhr.open("post", "example.php", true);
// 模拟成表单的提交
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
// 拼接出键值对字符串
xhr.send(content.join("&"));

实际上,键值对的内容和Get时一致,只是不需要?开头。

XMLHttpRequest 2级

老版XHR在使用中有如下缺点:

  • 只支持文本数据的传送,无法用来读取和上传二进制文件。
  • 传送和接收数据时,没有进度信息,只能提示有没有完成。
  • 受到"同域限制"(Same Origin Policy),只能向同一域名的服务器请求数据。

针对这个问题,W3C委员会提出了XHR 2级,作为功能加强,做出了如下增强:

  • 可以设置HTTP请求的时限。
  • 可以使用FormData对象管理表单数据。
  • 可以上传文件。
  • 可以请求不同域名下的数据(跨域请求)。
  • 可以获取服务器端的二进制数据。
  • 可以获得数据传输的进度信息。

下面我们就各个方面来看一下2级的XHR。

超时设置

有时,ajax操作很耗时,而且无法预知要花多少时间。如果网速很慢,用户可能要等很久。

新版本的XMLHttpRequest对象,增加了timeout属性,可以设置HTTP请求的时限。

xhr.timeout = 3000;

上面的语句,将最长等待时间设为3000毫秒。过了这个时限,就自动停止HTTP请求。与之配套的还有一个timeout事件,用来指定回调函数。

 xhr.ontimeout = function(event){
alert('请求超时!');
}

表单数据

ajax操作往往用来传递表单数据。为了方便表单处理,HTML 5新增了一个FormData对象,可以模拟表单。

首先,新建一个FormData对象。

var formData = new FormData();

然后,为它添加表单项。

 formData.append('username', '张三');
formData.append('id', 123456);

最后,直接传送这个FormData对象。这与提交网页表单的效果,完全一样。

xhr.send(formData);

FormData对象也可以用来获取网页表单的值。

 var form = document.getElementById('myform');
var formData = new FormData(form);
formData.append('secret', '123456'); // 添加一个表单项
xhr.open('POST', form.action);
xhr.send(formData);

上传文件

新版XMLHttpRequest对象,不仅可以发送文本信息,还可以上传文件。

假定files是一个"选择文件"的表单元素(input[type="file"]),我们将它装入FormData对象。

 var formData = new FormData();
for (var i = 0; i < files.length;i++) {
formData.append('files[]', files[i]);
}

然后,发送这个FormData对象。

xhr.send(formData);

跨域资源共享(CORS)

新版本的XMLHttpRequest对象,可以向不同域名的服务器发出HTTP请求。这叫做"跨域资源共享"(Cross-origin resource sharing,简称CORS)。

使用"跨域资源共享"的前提,是浏览器必须支持这个功能,而且服务器端必须同意这种"跨域"。如果能够满足上面的条件,则代码的写法与不跨域的请求完全一样。

我们下一篇笔记会详细讨论跨域的问题。

处理二进制数据

老版本的XMLHttpRequest对象,只能从服务器取回文本数据(否则它的名字就不用XML起首了),新版则可以取回二进制数据。

而处理二进制数据有两种方法,下面我们分别来看一下。

改写MIMEType

改写数据的MIMEType,将服务器返回的二进制数据伪装成文本数据,并且告诉浏览器这是用户自定义的字符集。

xhr.overrideMimeType("text/plain; charset=x-user-defined");

然后,用responseText属性接收服务器返回的二进制数据。

var binStr = xhr.responseText;

由于这时,浏览器把它当做文本数据,所以还必须再一个个字节地还原成二进制数据。

 for (var i = 0, len = binStr.length; i < len; ++i) {
var c = binStr.charCodeAt(i);
var byte = c & 0xff;
}

最后一行的位运算"c & 0xff",表示在每个字符的两个字节之中,只保留后一个字节,将前一个字节扔掉。原因是浏览器解读字符的时候,会把字符自动解读成Unicode的0xF700-0xF7ff区段。

responseType属性

从服务器取回二进制数据,较新的方法是使用新增的responseType属性。如果服务器返回文本数据,这个属性的值是"TEXT",这是默认值。较新的浏览器还支持其他值,也就是说,可以接收其他格式的数据。

你可以把responseType设为blob,表示服务器传回的是二进制对象。

 var xhr = new XMLHttpRequest();
xhr.open('GET', '/path/to/image.png');
xhr.responseType = 'blob';

接收数据的时候,用浏览器自带的Blob对象即可。

var blob = new Blob([xhr.response], {type: 'image/png'});

注意,是读取xhr.response,而不是xhr.responseText。

你还可以将responseType设为arraybuffer,把二进制数据装在一个数组里。

 var xhr = new XMLHttpRequest();
xhr.open('GET', '/path/to/image.png');
xhr.responseType = "arraybuffer";

接收数据的时候,需要遍历这个数组。

 var arrayBuffer = xhr.response;
if (arrayBuffer) {
var byteArray = new Uint8Array(arrayBuffer);
for (var i = 0; i < byteArray.byteLength; i++) {
// do something
}
}

进度信息

新版本的XMLHttpRequest对象,传送数据的时候,有一个progress事件,用来返回进度信息。

它分成上传和下载两种情况。下载的progress事件属于XMLHttpRequest对象,上传的progress事件属于XMLHttpRequest.upload对象。

我们先定义progress事件的回调函数。

xhr.onprogress = updateProgress;
xhr.upload.onprogress = updateProgress;

然后,在回调函数里面,使用这个事件的一些属性。

 function updateProgress(event) {
if (event.lengthComputable) {
var percentComplete = event.loaded / event.total;
}
}

上面的代码中,event.total是需要传输的总字节,event.loaded是已经传输的字节。如果event.lengthComputable不为真,则event.total等于0。

与progress事件相关的,还有其他五个事件,可以分别指定回调函数:

  • load事件:传输成功完成。
  • abort事件:传输被用户取消。
  • error事件:传输中出现错误。
  • loadstart事件:传输开始。
  • loadEnd事件:传输结束,但是不知道成功还是失败。

HTML5学习笔记(二十七):Ajax的更多相关文章

  1. HTML5学习笔记二 HTML基础

    一.HTML 标题 HTML 标题(Heading)是通过<h1> - <h6> 标签来定义的. <h1>标题一</h1> <h2>标题二& ...

  2. HTML5学习笔记 二:article和section

    在HTML5中,article可以看做特殊种类的section,它比section更强调独立性. section元素强调分段或分块,而article强调独立性: 如果一块内容相对独立.完整,应该使用a ...

  3. HTML5学习笔记<二>:元素,属性,格式化

    HTML元素 元素是指从开始标签到结束标签的所有代码. 开始(开放)标签 元素内容 结束(闭合)标签 <p> this is my web page </p> 没有内容的 HT ...

  4. Java基础学习笔记二十七 DBUtils和连接池

    DBUtils 如果只使用JDBC进行开发,我们会发现冗余代码过多,为了简化JDBC开发,本案例我们讲采用apache commons组件一个成员:DBUtils.DBUtils就是JDBC的简化开发 ...

  5. HTML5学习笔记(十七):访问器和class关键字

    访问器 在ECMAScript5中,提供了Object.defineProperty的方法,我们可以通过该方法来控制属性的更多权限. 属性类型 我们先看一段定义属性的代码: var person = ...

  6. angular学习笔记(二十七)-$http(5)-使用$http构建RESTful架构

    在angular中有一个特别为RESTful架构而定制的服务,是在$http的基础上进行了封装. 但是为了学习,我们先看看用直接$http是如何构建RESTful架构的: 假设有一个银行卡的列表.需要 ...

  7. Java学习笔记二十七:Java中的抽象类

    Java中的抽象类 一:Java抽象类: 在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就 ...

  8. PHP学习笔记二十七【重写】

    <?php // class Animal{ public $name; protected $price; function cry(){ echo "动物在叫....<br/ ...

  9. AJax 学习笔记二(onreadystatechange的作用)

    AJax 学习笔记二(onreadystatechange的作用) 当发送一个请求后,客户端无法确定什么时候会完成这个请求,所以需要用事件机制来捕获请求的状态XMLHttpRequest对象提供了on ...

  10. kvm虚拟化学习笔记(二)之linux kvm虚拟机安装

    KVM虚拟化学习笔记系列文章列表----------------------------------------kvm虚拟化学习笔记(一)之kvm虚拟化环境安装http://koumm.blog.51 ...

随机推荐

  1. VSTS 免费代码git/tfs托管体验-使用代码云托管

    虽然各种代码托管平台很多.真正免费的私有仓储 却很少.微软的东西还是值得一用.免费版,5个用户.够了. 申请地址: https://www.visualstudio.com/zh-hans/free- ...

  2. 播布客视频PIT专用播放器MBOO2015

    播布客视频,还是挺不错... 很多视频都是pit后缀的,需要用MBOO2015才可以打开... 00.MB2015软件 01.视频样例 02.download 链接: https://pan.baid ...

  3. rhel7.x配置本地yum

    转载:http://www.mvpbang.com/articles/2017/12/22/1513948827684.html rhel7.x配置本地yum 环境: centos7.4 vmarew ...

  4. [POST] What Is the Linux fstab File, and How Does It Work?

    If you’re running Linux, then it’s likely that you’ve needed to change some options for your file sy ...

  5. iOS如何在iTunes网站查看并下载APP的dsym文件

    有时需要拿到app的dsym符号表文件,恰巧本地的构建版本文件已经不在了,那么我们还可以在iTunes那边获取到. 步骤不复杂: 1.登陆itunes网站 https://itunesconnect. ...

  6. 严重: The web application [] registered the JDBC driver [com.microsoft.sqlserver.jdbc.SQLServerDriver] but failed to unregister it when the web application was stopped. To prevent a memory leak, the JDB

    idea项目启动报如下错误, 网上的方法都试了都没用, 一直没解决, 干掉项目, 重新从svn检出就好了...坑 啊 Root WebApplicationContext: initializatio ...

  7. 支持向量机-SVM 学习

    一 .支持向量机(SVM) 1.1 符号定义 标签 y 不再取 0 或 1,而是: y∈{-1, 1} 定义函数: 向量,没有第 0 个维度,b 为截距,预测函数定义为: 1.2 函数间隔与几何间隔 ...

  8. mytop安装,使用mytop监控MySQL性能

    本文主要描述mytop安装,安装过程中可能出现的报错,以及使用mytop监控MySQL性能. 欢迎转载,请注明作者.出处. 作者:张正 blog:http://space.itpub.net/2635 ...

  9. 【Struts2】自定义拦截器interceptors

    下面给一张图片表示Struts2拦截器的处理流程. 通过这个流程图,我们可以看出一个完整的请求大概的过程为: 请求 -->filter 控制器 --> 拦截器 1/ 拦截器 2--> ...

  10. ExpandoObject与DynamicObject的使用 RabbitMQ与.net core(一)安装 RabbitMQ与.net core(二)Producer与Exchange ASP.NET Core 2.1 : 十五.图解路由(2.1 or earler) .NET Core中的一个接口多种实现的依赖注入与动态选择看这篇就够了

    ExpandoObject与DynamicObject的使用   using ImpromptuInterface; using System; using System.Dynamic; names ...