深入理解Ajax

一、了解Ajax

1、背景:

在 2005 年,Google 通过其 Google Suggest 使 AJAX 变得流行起来。

Google Suggest 使用 AJAX 创造出动态性极强的 web 界面:当您在谷歌的搜索框输入关键字时,JavaScript 会把这些字符发送到服务器,然后服务器会返回一个搜索建议的列表。

2、介绍:

AJAX 是一种在无需重新加载整个网页的情况下,能够更新部分网页的技术。

AJAX = Asynchronous JavaScript and XML(异步的 JavaScript 和 XML)。

AJAX 不是新的编程语言,而是一种使用现有标准的新方法。

AJAX 最大的优点是在不重新加载整个页面的情况下,可以与服务器交换数据并更新部分网页内容。

AJAX 不需要任何浏览器插件,但需要用户允许JavaScript在浏览器上执行。

3、什么是ajax

AJAX是异步的JavaScript和XML(Asynchronous JavaScript And XML)。简单点说,就是使用 XMLHttpRequest 对象与服务器通信。 它可以使用JSON,XML,HTML和text文本等格式发送和接收数据。AJAX最吸引人的就是它的“异步”特性,也就是说它可以在不重新刷新页面的情况下与服务器通信,交换数据,或更新页面。

你可以使用AJAX最主要的两个特性做下列事:

  • 在不重新加载页面的情况下发送请求给服务器。
  • 接受并使用从服务器发来的数据。

4、工作原理:

AJAX

二、XMLHttpRequest 对象

怎样发送http请求?为了使用JavaScript向服务器发送一个http请求,你需要一个包含必要函数功能的对象实例XMLHttpRequest。

XMLHttpRequest 对象正是 AJAX 编程的实现方式。XMLHttpRequest对象用于与服务器交互。通过 XMLHttpRequest 可以在不刷新页面的情况下请求特定 URL,获取数据。这允许网页在不影响用户操作的情况下,更新页面的局部内容。

在此之前,都是通过在浏览器窗口输入url来获取整个页面数据,而ajax可以通过JavaScript代码创建XMLHTTPRequest对象来发送请求,与服务器交互,实现页面局部刷新页面。

所有现代浏览器(IE7+、Firefox、Chrome、Safari 以及 Opera)均内建 XMLHttpRequest 对象。而老版本的 Internet Explorer (IE5 和 IE6)使用 ActiveXObject对象。效果等同,只是浏览器内部实现的对象名字不一样。

怎么处理服务器的响应数据?

发送一个请求后,你会收到响应。在这一阶段,你要告诉XMLHttpRequest 请求对象是由哪一个JavaScript函数处理响应,onreadystatechange事件监听响应,当请求状态改变时调用函数。

那么XMLHTTPRequest对象是如何来发送数据和处理响应的呢?

ajax是和网络有关的,所以XMLHTTPRequest对象包含了一切与处理请求头、响应头、状态码等等相关操作的属性、方法以及事件。

创建XMLHTTPRequest对象,发送请求,直到服务器响应,返回响应数据,都在XMLHTTPRequest对象上。比如服务器响应的数据对应XMLHTTPRequest对象的responseText属性;响应的状态码200,对应XMLHTTPRequest对象的 status 属性。

XMLHTTPRequest对象上方法、属性、事件:

方法:open()、send()、setRequestHeader()、abort()、getAllResponseHeaders()

属性:readyState状态、status状态码 200 代表一个成功的请求、statusText状态文本,200对应OK、timeout超时时间被终止请求、

response、responseText、responseType、responseURL、responseXML、

upload

事件:onreadystatechange、onload、ontimeout


上述提到,IE5和IE6浏览器是通过ActiveXObject对象来实现Ajax的,所以要进行兼容性的处理(IE8都没有多人用了,还有人用IE5 IE6?根本不用兼容)

1、创建XHR实例对象

var XHR = new 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() 初始化http请求

设置以get或post的方式去打开一个url地址(open方法只是设置,send方法才是发送http请求)

xhrReq.open(method, url, async);

参数:

method:GET或POST

url:请求地址

async:一个可选的布尔参数,表示是否异步执行操作,默认为true。——异步

如果值为falsesend()方法直到收到答复前不会返回。——同步。注意:主线程上的同步请求,程序要等会ajax的响应结束,而不会往下执行。很容易破坏用户体验,应该避免;实际上,许多浏览器已完全弃用主线程上的同步XHR支持。在 Worker中允许同步请求

3、MLHttpRequest.setRequestHeader() 设置请求头

我们通过http协议传输数据,客户端在发送数据时,要设置该条数据流是怎么的文件类型,以及要经过怎么样格式编码。

XMLHttpRequest.setRequestHeader() 是设置HTTP请求头部的方法。此方法必须在  open() 方法和 send()   之间调用。 

比如:设置 Content-Type头部来指定数据流的MIME类型。

xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");

自定义一些header属性进行跨域请求时,可能会遇到"not allowed by Access-Control-Allow-Headers in preflight response",你可能需要在你的服务端设置"Access-Control-Allow-Headers"。

如果没有设置 Accept 属性,则此发送出send() 的值为此属性的默认值*/* 。

安全起见,有些请求头的值只能由浏览器客户端(user agent)设置,开发者设置的请求头会被视为无效。

Mime类型表:https://tool.oschina.net/commons

以下列举几个:

.txt      text/plain

.html   text/html

.jpg    image/jpeg

.png   image/png

.mp4   video/mpeg4

.css   text/css

.js     application/x-javascript

比如:发送json数据

XHR.setRequestHeader("Content-type","application/json; charset=utf-8")

 发送纯文本数据

XHR.setRequestHeader("Content-type","text/plain; charset=utf-8")

 参考阅读文章:https://blog.csdn.net/iamduoluo/article/details/7215639

4、XMLHttpRequest.send() 发送http请求

https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest/send

XMLHttpRequest.send() 方法用于发送 HTTP 请求。接收一个参数,作为发送给服务器的数据;如果是get请求,不接收参数,即不发送任何数据。

发送二进制内容的最佳方法(如上传文件)是使用一个与send()方法结合的 ArrayBufferView 或者Blobs

send() 方法的参数可以是任何你想发送给服务器的内容,如果是 POST 请求的话。发送表单数据时应该用服务器可以解析的格式,像查询语句:

"name=value&anothername="+encodeURIComponent(myVar)+"&so=on"

或者其他格式, 类似 multipart/form-data,JSON,XML等。

如果你使用 POST 数据,那就需要设置请求的MIME类型。比如,在调用 send() 方法获取表单数据前要有下面这个:

httpRequest.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');

ajax的post请求,发送json数据:

            var xhr = new XMLHttpRequest();
            xhr.open('POST', 'http://localhost:8080/websocket');

            xhr.onreadystatechange = function (event) {
                console.log(xhr.readyState);
                if (xhr.readyState == 4 && xhr.status == 200) {
                    console.log(xhr)
                }
            }
            xhr.setRequestHeader("Content-type","application/json; charset=utf-8")
            var data = {
                name: 'zhu',
                age: 24
            }
            xhr.send(JSON.stringify(data))

5、XMLHttpRequest.onreadystatechange

XMLHttpRequest对象上的readyState属性,随着此次ajax客户端和服务器交互数据,动态的改变readyState属性,不同的值,代表了ajax响应的不同状态。

readyState属性值的状态表示:创建XMLHttpRequest实例对象——>open方法创建http请求——>send方法发送http请求——>服务器正在响应——>服务器响应完成

从创建XMLHttpRequest对象开始,到发送数据、接收数据、XMLHttpRequest对象一共会经历以下5中状态。具体状态值对应的情况如下:

readyState状态描述
0UNSENT代理被创建,但尚未调用 open() 方法。0 (未初始化) or (请求还未初始化)
1OPENEDopen() 方法已经被调用。1 (正在加载) or (已建立服务器链接)
2HEADERS_RECEIVEDsend() 方法已经被调用,并且头部和状态已经可获得。2 (加载成功) or (请求已接受)
3LOADING下载中; responseText 属性已经包含部分数据。3 (交互) or (正在处理请求)
4DONE下载操作已完成。4 (完成) or (请求已完成并且响应已准备好)

再来看readystatechange事件:

通过XMLHttpRequest.onreadystatechange监听readyState属性值的改变,只要readyState属性发生变化(0——>1,1——>2,2——>3,3——>4),就会触发readystatechange事件。而我们要监听的正是readyState为4的情况。

只要 readyState 属性发生变化,就会调用readystatechange事件相应的处理函数XMLHttpRequest.onreadystatechange 会在 XMLHttpRequest 的readyState 属性发生改变时触发 readystatechange 事件的时候被调用。

语法:当 readyState 的值改变的时候,callback 函数会被调用

XMLHttpRequest.onreadystatechange = callback;
var xhr= new XMLHttpRequest(),
    method = "GET",
    url = "https://www.easy-mock.com/mock/5b4c4032189fc57b63eb8410/example/mock";

xhr.open(method, url, true);
xhr.onreadystatechange = function () {
  if(xhr.readyState === 4 && xhr.status === 200) {
    console.log(xhr.responseText)
  }
}
xhr.send();

 XHR.readyState == 4 表示发送ajax请求成功且服务器响应成功。

XHR.status == 200 表示服务器成功响应了数据。

XHR.responseText  服务器响应的数据挂载在XMLHttpRequest对象的responseText属性上

在检查完请求状态和HTTP响应码后, 你就可以用服务器返回的数据做任何你想做的了。你有两个方法去访问这些数据:

  • httpRequest.responseText – 服务器以文本字符的形式返回
  • httpRequest.responseXML – 以 XMLDocument 对象方式返回,之后就可以使用JavaScript来处理

6、XMLHttpRequest.timeout 和 timeout事件,ajax请求超时

(1)XMLHttpRequest.timeout属性  兼容IE8

设置超时时间,当ajax请求超时达到这个时间,就自动终止该条请求。浏览器在请求某个资源的时候,会一直在某个时间段内一直请求该资源,直到浏览器默认的超时时间,才会终止请求。

是一个无符号长整型数,代表着一个请求在被自动终止前所消耗的毫秒数。默认值为 0,意味着没有超时。为什么默认值是0?发送ajax请求,经常网络传输,经过服务器的处理,在正常的带宽时间内得到数据,就是没有超时。但是如果网络较慢,某些处理的环境会花费超过阈值时间,即为超时。

超时并不应该用在一个 document environment 中的同步 XMLHttpRequests  请求中,否则将会抛出一个 InvalidAccessError 类型的错误。

当超时发生, timeout 事件将会被触发。在IE中,超时属性可能只能在调用 open() 方法之后且在调用 send() 方法之前设置。

(2)timeout 事件   兼容IE10

当进度由于预定时间到期而终止时,会触发timeout 事件。

timeout 事件实现了 ProgressEvent 接口,它继承自 Event — 它拥有在这个接口上定义的属性和方法。该事件上包括了ProgressEvent事件信息。

ProgressEvent 接口是测量如 HTTP 请求(一个XMLHttpRequest,或者一个 <img><audio><video><style> 或 <link> 等底层资源的加载)等底层流程进度的事件。

var XHR;
if (window.XMLHttpRequest) {
  //  IE7+, Firefox, Chrome, Opera, Safari 浏览器执行代码
  XHR = new XMLHttpRequest();
} else {
  // IE6, IE5 浏览器执行代码
  XHR = new ActiveXObject("Microsoft.XMLHTTP");
}
XHR.onreadystatechange = function (event) {
  console.log(XHR.readyState);
  if (XHR.readyState == 4 && XHR.status == 200) {
    console.log(XHR)
  }
  
}
XHR.ontimeout = function(){
  console.log("超时100ms而终止ajax请求")
}
XHR.open("GET", "http://localhost:8080/about.html");
XHR.setRequestHeader("Content-type", "text/html, charset=utf-8");
XHR.timeout = 100; //设置超时时间100ms
XHR.send();

7、MLHttpRequest.onload  请求完成时触发load事件

不管成功还是失败,整个请求完成,即触发load事件。如果请求没有完成而终止,不触发load事件。比如由于超时timeout终止了请求,或者abort终止请求,导致ajax请求没有完成,不会触发load事件。

8、XMLHttpRequest.abort() 和 XMLHttpRequest的abort事件

(1)XMLHttpRequest.abort() 

如果该请求已被发出,XMLHttpRequest.abort() 方法将终止该请求。当一个请求被终止,它的  readyState 将被置为 XMLHttpRequest.UNSENT (0),并且请求的 status 置为 0。

(2)XMLHttpRequest.onabort   兼容性IE10

当一个请求终止时 abort 事件被触发,比如程序执行 XMLHttpRequest.abort()。好像只监听abort方法触发的终止事件。

什么时候能执行abort方法,而成功的触发abort事件呢?在onreadystatechange事件中无法执行abort方法而终止请求;在timeout事件中无法执行abort方法而终止请求。好像不太容易终止掉。

测试时,在这种情况下,成功终止了:

var XHR;
if (window.XMLHttpRequest) {
  //  IE7+, Firefox, Chrome, Opera, Safari 浏览器执行代码
  XHR = new XMLHttpRequest();
} else {
  // IE6, IE5 浏览器执行代码
  XHR = new ActiveXObject("Microsoft.XMLHTTP");
}
XHR.onreadystatechange = function (event) {
  if (XHR.readyState == 4 && XHR.status == 200) {
    console.log(XHR)
  }
  
}
// XHR.ontimeout = function(event){
//   console.log(XHR);
// }
XHR.onload = function(event){
  console.log(event)
}
XHR.onabort = function(){
  console.log("ajax请求终止")
}
XHR.open("GET", "http://localhost:8080/about.html");
XHR.setRequestHeader("Content-type", "text/html, charset=utf-8");
// XHR.timeout = 100; //设置超时时间100ms
XHR.send();
console.log(XHR.readyState);  //1 
XHR.abort();

三、简单的实例:

发送ajax请求,获取一个html数据

var XHR;
if (window.XMLHttpRequest) {
  //  IE7+, Firefox, Chrome, Opera, Safari 浏览器执行代码
  XHR = new XMLHttpRequest();
} else {
  // IE6, IE5 浏览器执行代码
  XHR = new ActiveXObject("Microsoft.XMLHTTP");
}
XHR.onreadystatechange = function (event) {
  console.log(XHR.readyState);
  if (XHR.readyState == 4 && XHR.status == 200) {
    console.log(XHR)
  }
  
}
XHR.open("GET", "http://localhost:8080/about.html");
XHR.setRequestHeader("Content-type", "text/html, charset=utf-8")
XHR.send();

 结果:response属性和responseText属性为响应数据

四、原生ajax与jquery封装的ajax对比

jquery的ajax官网:https://api.jquery.com/jQuery.ajax/#jQuery-ajax-settings

好消息是原生ajax代码兼容所有最新的主流浏览器。而坏消息是使用起来十分复杂。的确令人恶心! 

jQuery 已经成为了解决 Ajax 的默认标准。

1、关于请求缓存问题

jquery的ajax默认会清除浏览器缓存(注意是当前ajax请求的缓存,不是浏览器所有的缓存)。查看 jQuery 文档,可知道 jQuery 在每个请求(GET)后面追加一个时间戳作为查询字符串。这在某个程度上让请求变得独一无二,并避免浏览器缓存。当然,你能在纯 Ajax 达到该目的,而且相当简单:都是在url请求后面添加时间戳

var bustCache = '?' + new Date().getTime();
oReq.open('GET', e.target.dataset.url + bustCache, true);

2、关于请求头

jQuery 私下帮你设置请求头部了,所以后端能检测这是一个 Ajax 请求。一般来说,后端并不关心 GET 请求是从哪而来,只要能返回正确的响应即可。当你相用同样的 web API 返回 Ajax 或 HTML 时,这就派上用场了。让我们看看如何通过原生 JavaScript 设置请求头部:

var oReq = new XMLHttpRequest();
oReq.open('GET', e.target.dataset.url, true);
oReq.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
oReq.send();

与此同时,我们在 服务端做一个检测,拿Node.js举例:

if (req.headers['x-requested-with'] === 'XMLHttpRequest') {
    res.writeHead(200, {'Content-Type': 'application/json'});
    res.end(JSON.stringify({message: 'Hello World!'}));
}

 正如你所看到的,原生 Ajax 是一个灵活且现代化的前端 API。你可以利用请求头部做很多事情,其中一种是版本控制。例如,我想让某个 web API 支持多个版本。但我又不想利用 URL,取而代之的是:通过设置请求头部,使客户端能选择它们想要的版本。所以,我们能这样设置请求头部:

oReq.setRequestHeader('x-vanillaAjaxWithoutjQuery-version', '1.0');

 然后,在后端写上相应代码:

if (req.headers['x-requested-with'] === 'XMLHttpRequest' &&
    req.headers['x-vanillaajaxwithoutjquery-version'] === '1.0') {
    // Send Ajax response
}

3、关于响应类型

responseText 默认返回的是字符串 ,但是原生ajax可以设置前端 API 所期望服务器返回的数据类型。比如:

var oReq = new XMLHttpRequest();
oReq.onload = function (e) {
    results.innerHTML = e.target.response.message;
};
oReq.open('GET', e.target.dataset.url, true);
oReq.responseType = 'json';
oReq.send();

这样我们就不必再对返回的纯文本解析为 JSON 了,我们能告诉 API 我们期待接收的数据类型。该特性几乎得到了所有最新主流浏览器的支持。当然,jQuery 会自动帮我们转为适当的类型。但遗憾的是,到 IE11 为止,开发团队仍未对 xhr.responseType='json' 进行支持。虽然该特性目前在 Microsoft Edge 得到支持。但这个 Bug 提出几乎两年了。 当然,可以这么做解决这个 IE兼容性的问题:

oReq.onload = function (e) {
    var xhr = e.target;
    if (xhr.responseType === 'json') {
        results.innerHTML = xhr.response.message;
    } else {
        results.innerHTML = JSON.parse(xhr.responseText).message;
    }
};

 需要注意的是,ajax需要前后端的相互配合,前端发送ajax请求,设置相关的请求,经过客户端的处理(有些设置不被客户端允许),发送到服务端,服务端对ajax请求进行验证处理后,响应数据。

原生ajax使用的比较少,只需要知道一些相关的方法api,接下来还需要研究:

(1)请求头、响应头等等报文。 

(2)jquery对ajax的实现和使用;

(3)cors跨域问题;

(4)Fetch

(5)cookie

(6)axios:基于promise的http库

(7)websocket双全工通讯协议

等等。。。

参考文章:

https://www.runoob.com/ajax/ajax-intro.html

https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest

https://segmentfault.com/a/1190000004100271

https://developer.mozilla.org/zh-CN/docs/Web/Guide/AJAX/Getting_Started

  • 6
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值