AJAX的原理和XMLHttpRequest的使用


AJAX的原理和XMLHttpRequest的学习笔记

零、参考文章

https://blog.csdn.net/qq_29569183/article/details/79259889

一、AJAX概述

AJAX全称为“Asynchronous JavaScript and XML”(异步JavaScript和XML)

Ajax的工作原理相当于在用户和服务器之间加了—个中间层(AJAX引擎),使用户操作与服务器响应异步化。并不是所有的用户请求都提交给服务器,像—些数据验证和数据处理等都交给Ajax引擎自己来做, 只有确定需要从服务器读取新数据时再由Ajax引擎代为向服务器提交请求。

AJAX是对XMLHttpRequest的封装。

二、实现步骤

2.1 实现一个AJAX异步调用和局部刷新,通常需要以下几个步骤:

  1. 创建XMLHttpRequest对象,也就是创建一个异步调用对象.
  2. 创建一个新的HTTP请求,并指定该HTTP请求的方法、URL及验证信息.
  3. 设置响应HTTP请求状态变化的函数.
  4. 发送HTTP请求.
  5. 获取异步调用返回的数据.
  6. 使用JavaScript和DOM实现局部刷新.

2.2具体实现步骤

(一)创建XMLHttpRequest对象

ie浏览器和非ie使用方式不同

var xmlHttpRequest;  //定义一个变量,用于存放XMLHttpRequest对象
function createXMLHttpRequest()    //创建XMLHttpRequest对象的方法
 {
    if(window.ActiveXObject)   //判断是否是IE浏览器
     {
        xmlHttpRequest = new ActiveXObject("Microsoft.XMLHTTP");  //创建IE浏览器中的XMLHttpRequest对象
       }
       else if(window.XMLHttpRequest)    //判断是否是Netscape等其他支持XMLHttpRequest组件的浏览器
       {
        xmlHttpRequest = new XMLHttpRequest();  //创建其他浏览器上的XMLHttpRequest对象
        }
}

(二)创建HTTP请求

XMLHttpRequest.open(method,URL,flag,name,password)
  • method:该参数用于指定HTTP的请求方法,一共有get、post、head、put、delete五种方法,常用的方法为get和post。
  • URL:该参数用于指定HTTP请求的URL地址,可以是绝对URL,也可以是相对URL。
  • flag:该参数为可选参数,参数值为布尔型。该参数用于指定是否使用异步方式。true表示异步方式、false表示同步方式,默认为true。
  • name:该参数为可选参数,用于输入用户名。如果服务器需要验证,则必须使用该参数。
  • password:该参数为可选参数,用于输入密码。如果服务器需要验证,则必须使用该参数。通常可以使用以下代码来访问一个网站文件的内容。

(三)设置响应HTTP请求状态变化的函数

XMLHttpRequest.open之后会根绝请求所处的阶段改变readyState的值

  • 未初始化状态。在创建完XMLHttpRequest对象时,该对象处于未初始化状态,此时XMLHttpRequest对象的readyState属性值为0。
  • 初始化状态。在创建完XMLHttpRequest对象后使用open()方法创建了HTTP请求时,该对象处于初始化状态。此时XMLHttpRequest对象的readyState属性值为1。
  • 发送数据状态。在初始化XMLHttpRequest对象后,使用send()方法发送数据时,该对象处于发送数据状态,此时XMLHttpRequest对象的readyState属性值为2。
  • 接收数据状态。Web服务器接收完数据并进行处理完毕之后,向客户端传送返回的结果。此时,XMLHttpRequest对象处于接收数据状态,XMLHttpRequest对象的readyState属性值为3。
  • 完成状态。XMLHttpRequest对象接收数据完毕后,进入完成状态,此时XMLHttpRequest对象的readyState属性值为4。此时接收完毕后的数据存入在客户端计算机的内存中,可以使用responseText属性或responseXml属性来获取数据。
监听状态改变onreadystatechange
//设置当XMLHttpRequest对象状态改变时调用的函数,注意函数名后面不要添加小括号
xmlHttpRequest.onreadystatechange = getData;
//定义函数
function getData()
{
      //判断XMLHttpRequest对象的readyState属性值是否为4,如果为4表示异步调用完成
      if(xmlHttpRequest.readyState == 4)
      {
                  //设置获取数据的语句
       }

}
判断status属性值是否等于200

但是,异步调用过程完毕,并不代表异步调用成功了,如果要判断异步调用是否成功,还要判断XMLHttpRequest对象的status属性值,只有该属性值为200,才表示异步调用成功,因此,要获取服务器返回数据的语句,还必须要先判断XMLHttpRequest对象的status属性值是否等于200,

(四)发送请求

在经过以上几个步骤的设置之后,就可以将HTTP请求发送到Web服务器上去了。发送HTTP请求可以使用XMLHttpRequest对象的send()方法,其语法代码如下所示:

XMLHttpRequest.send(data)
参数形式

其中data是个可选参数,如果是get请求的数据写在url中,那这里即可以使用null来替代。如果是post请求,data参数的格式与在URL中传递参数的格式类似,以下代码为一个send()方法中的data参数的示例:

name=myName&value=myValue

(五) level2 完整实例

function sendAjax() {
  //构造表单数据
  var formData = new FormData();
  formData.append('username', 'johndoe');
  formData.append('id', 123456);
  //创建xhr对象 
  var xhr = new XMLHttpRequest();
  //设置xhr请求的超时时间
  xhr.timeout = 3000;
  //设置响应返回的数据格式
  xhr.responseType = "text";
  //创建一个 post 请求,采用异步
  xhr.open('POST', '/server', true);
  //注册相关事件回调处理函数
  xhr.onload = function(e) { 
    if(this.status == 200||this.status == 304){
        alert(this.responseText);
    }
  };
  xhr.ontimeout = function(e) { ... };
  xhr.onerror = function(e) { ... };
  xhr.upload.onprogress = function(e) { ... };
  
  //发送数据
  xhr.send(formData);
}

三、XMLHttpRequest的其他操作

3.1如何设置request header

void setRequestHeader(DOMString header, DOMString value);
  • 方法的第一个参数 header 大小写不敏感,即可以写成content-type,也可以写成Content-Type,甚至写成content-Type;
  • Content-Type的默认值与具体发送的数据类型有关,请参考本文【可以发送什么类型的数据】一节;
  • setRequestHeader必须在open()方法之后,send()方法之前调用,否则会抛错;
  • setRequestHeader可以调用多次,最终的值不会采用覆盖override的方式,而是采用追加append的方式。

下面是一个示例代码:

var client = new XMLHttpRequest();
client.open('GET', 'demo.cgi');
client.setRequestHeader('X-Test', 'one');
client.setRequestHeader('X-Test', 'two');
// 最终request header中"X-Test"为: one, two
client.send();

3.2如何获取response header

xhr提供了2个用来获取响应头部的方法:getAllResponseHeaders和getResponseHeader。前者是获取 response 中的所有header 字段,后者只是获取某个指定 header 字段的值。另外,getResponseHeader(header)的header参数不区分大小写。

DOMString getAllResponseHeaders();
DOMString getResponseHeader(DOMString header);

注意这两个方法只能获取一部分浏览器认为安全的头。

3.3上传文件

新版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);

3.4跨域资源共享(CORS)

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

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

xhr.open('GET', 'http://other.server/and/path/to/script');

目前,除了IE 8和IE 9,主流浏览器都支持CORS,IE 10也将支持这个功能。服务器端的设置,请参考《Server-Side Access Control》。

[外链图片转存失败(img-s0YZ2z9I-1566183878332)(0730D06C1A38451FA089EC3C57F297C8)]

跨域的三个场景

(一)简单请求,不需要请求2次,只使用GET、POST进行的请求,请求没有自定义的请求头。
满足的条件

不会触发CORS预检的请求(所谓的“简单请求”)是满足以下所有条件的请求:

  • 唯一允许的方法是:
    • GET
    • HEAD
    • POST
  • 除了由用户代理自动设置的标头(例如,Connection,User-Agent,或任何与所述抓取规格为“禁止的标题名称”定义名称其它标题的),其允许被手动设置仅标头是那些Fetch规范将其定义为“CORS安全列表请求标头”,它们是:
    • Accept
    • Accept-Language
    • Content-Language
    • Content-Type (但请注意下面的附加要求)
    • Last-Event-ID
    • DPR
    • Downlink
    • Save-Data
    • Viewport-Width
    • Width
  • Content-Type标题的唯一允许值是:
    • application/x-www-form-urlencoded
    • multipart/form-data
    • text/plain

大部分时候其实指的是默认的请求,这样不会出现触发检测。

客户端代码
var xhr=new XMLHttpRequest();
xhr.open('get','http://localhost:3000');
//xhr.open('post','http://localhost:3000');
xhr.send();
服务端代码
var koa = require('koa');
var app = koa();
app.use(function*() {
    this.set('Access-Control-Allow-Origin', '*');
    this.body = this.method == 'POST' ? 'is Post' : 'is Get';
});
app.listen(3000);

[外链图片转存失败(img-ooRfsrOy-1566183878333)(E299A40A65CC4DE7AAB600A020F1C648)]

*Access-Control-Allow-Origin为设置允许请求的来源地址,代表全部

不满足简单请求的情况

需要先OPTIONS请求来检验是否允许请求,然后再次请求,可以设置各种头,OPTIONS会由xhr自动处理,但是会增加请求成本,设置Access-Control-Max-Age可以减少检测的频率。

服务器需要设置允许自定义的请求头。

客户端代码
var xhr=new XMLHttpRequest();
xhr.open('get','http://localhost:3000');
//xhr.open('post','http://localhost:3000');
xhr.setRequestHeader('Foo','http://localhost:3000');
xhr.send();
服务端代码
var koa = require('koa');
var app = koa();
app.use(function*() {
    this.set('Access-Control-Allow-Origin', '*');
    this.set('Access-Control-Allow-Headers', 'foo');
    this.body = this.method == 'POST' ? 'is Post' : 'is Get';
});
app.listen(3000);

Access-Control-Allow-Headers大小中定义的请求头的大小写不敏感,用逗号隔开

提交cookie信息情况

XMLHttpRequest必须设置的标志,以便使用Cookie进行调用,即withCredentials布尔值。默认情况下,调用不使用Cookies。由于这是一个简单的GET请求,这不是预检,但浏览器将拒绝不具有任何的响应Access-Control-Allow-Credentials: true报头,并没有提供给调用web内容的响应。

认证请求和通配符
在响应有证书请求时,服务器必须在Access-Control-Allow-Origin标题的值中指定一个源,而不是指定*通配符。

由于上述示例中的请求标头包含Cookie标头,因此如果Access-Control-Allow-Origin标头的值为*,则请求将失败。但它不会失败:因为Access-Control-Allow-Origin头部的值是http://foo.example

客户端代码
var xhr=new XMLHttpRequest();
xhr.open('get','http://localhost:3000');
//xhr.open('post','http://localhost:3000');
xhr.withCredentials=true;
xhr.send();
服务端代码
var koa = require('koa');
var app = koa();
app.use(function*() {
    this.set('Access-Control-Allow-Origin', 'http://foo.example');
    this.set('Access-Control-Allow-Credentials', true);
    this.body = this.method == 'POST' ? 'is Post' : 'is Get';
});
app.listen(3000);

Access-Control-Allow-Credentials必须为true且Access-Control-Allow-Origin必须是指定的来源地址,其中的Cookie只会提交http://localhost:3000下的Cookie信息

其他的服务器返回头
字段说明
Access-Control-Expose-Headers设置相应返回的时候可以读取的头,不设置的时候能读取Content-Type
Access-Control-Allow-Methods是逗号分隔的一个字符串,表明服务器支持的所有跨域请求的方法
Access-Control-Max-Age检测结果缓存时间

例如:对于在头请求中添加了token,以至于请求变成复杂请求,由于不想让客户端每个都发一个OPTIONS预检测,后台可以配置Access-Control-Max-Age来缓存预检测结果,这样在一定时间内,就是第一次发送请求的时候预检测,后面的就直接发送请求了。

3.5 接收二进制数据(方法A:改写MIMEType)

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

这里又分成两种做法。

较老的做法是改写数据的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区段。

接收二进制数据二(方法B: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

  }
}

更详细的讨论,请看Sending and Receiving Binary Data。

3.6 查看进度信息

新版本的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事件:传输结束,但是不知道成功还是失败。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值