在上一篇文章中咱们知道,AJAX是一系列技术的统称。在本篇中咱们将更进一步,详细解释如何使用Ajax技术在项目中获取数据。而为了解释清楚,咱们首先要搞清楚咱们是从哪里获取数据的,其次咱们关注的才是获取数据的具体方式。php
咱们知道AJAX用来在项目中以阻止页面刷新的方式获取数据,那么数据从哪里来呢?咱们又怎么知道如何获取这些数据?答案是咱们一般使用API与各式各样的数据库交互。ajax
“API”是“Application Programming Interface”(即:应用程序接口)的缩写,你能够想象一些数据是开放的而且在等待被使用,而咱们获取这些数据的方式即是使用API。API一般的形式是一个URL,并提供指定的参数名和参数值用来帮助你定位所要获取的数据。数据库
还记得咱们提过AJAX须要服务器端的相应设置吗?咱们以后会再来谈这一点。浏览器
让咱们先把服务器端的设置抛在一边,聚焦AJAX技术的核心环节:XMLHttpRequest
对象。安全
XMLHttpRequest
对象是浏览器提供的一个API,用来顺畅地向服务器发送请求并解析服务器响应,固然整个过程当中,浏览器页面不会被刷新。它将是本文接下来的主角,让咱们先站在较高的层次,对该对象有一个全局的概览:服务器
XMLHttpRequest
只是一个JavaScript对象,确切的说,是一个构造函数。换句话说,它一点也不神秘,它的特殊之处只在于它是由客户端(即浏览器)提供的(而不是JavaScript原生的),除此以外,它有属性,有方法,须要经过new
关键字进行实例化,咱们只需掌握它们就好;XMLHttpRequest
对象是不断被扩展的。随着XML对象被普遍的接收,W3C也开始着手制定相应的标准来规范其行为。目前,XMLHttpRequest
有两个级别:1级提供了XML对象的实现细节,2级进一步发展了XML对象,额外添加了一些方法,属性和数据类型。可是,并非全部浏览器都实现了XML对象2级的内容(并不意外,对吧?);让咱们先从剖析XMLHttpRequest
实例的属性和方法开始,先建立一个XML对象的实例:网络
const xhr = new XMLHttpRequest()
复制代码
该实例的属性,方法有:app
.open()
:准备启动一个AJAX请求;.setRequestHeader()
:设置请求头部信息;.send()
:发送AJAX请求;.getResponseHeader()
: 得到响应头部信息;.getAllResponseHeader()
:得到一个包含全部头部信息的长字符串;.abort()
:取消异步请求;.responseText
:包含响应主体返回文本;.responseXML
:若是响应的内容类型时text/xml
或application/xml
,该属性将保存包含着相应数据的XML DOM文档;.status
:响应的HTTP状态;.statusText
:HTTP状态的说明;.readyState
:表示“请求”/“响应”过程的当前活动阶段另外,浏览器还为该对象提供了一个onreadystatechange
监听事件,每当XML实例的readyState
属性变化时,就会触发该事件的发生。异步
至此,关于XMLHttpRequest实例对象的属性方法就所有罗列完毕了,接下来,咱们将更进一步的探究如何使用这些方法,属性完成发送AJAX请求的流程。ide
要想与服务器交互,咱们首先须要回答如下问题:
GET
或POST
;而XMLHttpRequest实例的.open()
方法的做用就是用来回答以上三个问题。.open()
方法接收三个参数:请求方式,请求URL地址和是否为异步请求的布尔值。
下面是一个.open()
方法调用的例子:
// 该段代码会启动一个针对“example.php”的GET同步请求。
xhr.open("get", "example.php", false)
复制代码
至关于开始作饭前,将工具准备齐备,将菜洗好,.open()
方法也一样出色地完成了发送AJAX请求的准备工做。
如今,让咱们再深刻聊聊一些准备工做的细节:
GET请求用于获取数据,有时候咱们须要获取的数据须要经过“查询参数”进行定位,在这种状况下,咱们会将查询参数追加到URL的末尾,令服务器解析。
查询参数是指一个由?
号起始,由&
符号分割的包含相应键值对的字符串。用来告知浏览器所要查询的特定资源。
const query = "example.php?name=tom&age=24" // "?name=tom&age=24"便是一个查询参数
复制代码
须要注意的是,查询字符串中每一个参数的名和值都必须使用encodeURIComponent()进行编码(这是由于URL中有些字符会引发歧义,例如“&”)。
POST请求用于向服务器发送应该被保存的数据,所以POST请求自然比GET请求多须要一份须要被保存的数据。那么这些数据应该放在何处呢?毕竟,咱们的.open()
方法接收的三个参数都没有合适的位置。
答案是须要发送的数据会做为.send()
方法的参数最终被发往服务器,该数据能够是任意大小,任意类型。
这里须要注意如下两点:
.send()
方法的参数是不可为空的,也就是说,对于不须要发送任何数据的GET请求,也须要在调用.send()
方法时,向其传入null
值;但好在咱们能够经过POST请求模拟表单提交,只须要简单两步:
Content-Type: application/x-www-form-urlencoded
(表单提交时的内容类型);.send()
方法;这里须要注意若使用相对路径,请求URL是相对于执行代码的当前页面。
人们一般认为AJAX是异步的,实际上并不是如此,AJAX是避免页面在获取数据后刷新的一种技术,至于等待服务器响应的方式是同步仍是异步,须要开发人员结合业务需求进行配置(虽然一般是异步的)。
你可能会好奇,何时咱们须要使用同步的AJAX?就我我的经验而言,彷佛很难找到相应的场景。Stack Overflow上有一个相似的问题,有兴趣的不妨点击查看。
最后咱们再简单解释一下“同步”等待响应与“异步”等待响应的区别:“同步”意味着一旦请求发出,任何后续的JavaScript代码不会再执行,“异步”则是当请求发出后,后续的JavaScript代码会继续执行,当请求成功后,会调用相应的回调函数。
每一个HTTP请求和响应都会带有相应的头部信息,包含一些与数据,收发者网络环境与状态等相关信息。XMLHttpRequest对象提供的.setRequestHeader()
方法为开发者提供了一个操做这两种头部信息的方法,并容许开发者自定义请求头的头部信息。
默认状况下,当发送AJAX请求时,会附带如下头部信息:
Accept
:浏览器可以处理的内容类型;Accept-Charset
: 浏览器可以显示的字符集;Accept-Encoding
:浏览器可以处理的压缩编码;Accept-Language
:浏览器当前设置的语言;Connection
:浏览器与服务器之间链接的类型;Cookie
:当前页面设置的任何Cookie;Host
:发出请求的页面所在的域;Referer
:发出请求的页面URI;User-Agent
:浏览器的用户代理字符串;注意,部分浏览器不容许使用.setRequestHeader()
方法重写默认请求头信息,所以自定义请求头信息是更加安全的方法:
// 自定义请求头
xhr.setRequestHeader("myHeader", "MyValue")
复制代码
到此为止,咱们已经彻底作好了发送请求的全部准备:利用.open()
方法肯定了请求方式,等待响应的方式和请求地址,甚至还经过.setRequestHeader()
自定义了响应头,接下来就到了最激动人心的时刻:使用.send()
方法,发送AJAX请求!
// 发送AJAX请求!
const xhr = new XMLHttpRequest()
xhr.open("get", "example.php", false)
xhr.setRequestHeader("myHeader", "goodHeader")
xhr.send(null)
复制代码
呃,简单的有些使人尴尬不是吗?换个POST请求试试看:
// 发送AJAX请求!
const xhr = new XMLHttpRequest()
xhr.open("post", "example.php", false)
xhr.setRequestHeader("myHeader", "bestHeader")
xhr.send(some_data)
复制代码
额..,总以为仍是差点什么?放轻松伙计,由于咱们只是发出了请求,尚未处理响应,咱们这就来看看它。
让咱们直接看看如何处理一个同步的GET请求响应:
const xhr = new XMLHttpRequest()
xhr.open("get", "example.php", false)
xhr.setRequestHeader("myHeader", "goodHeader")
xhr.send(null)
// 因为是同步的AJAX请求,所以只有当服务器响应后才会继续执行下面的代码
// 所以xhr.status的值必定不为默认值
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {
alert(xhr.responseText)
} else {
alert("Request was unsuccessful: " + xhr.status)
}
复制代码
上面的代码不难理解,咱们经过以前提到的xhr.status
属性(若是你忘记了,它存储着响应的HTTP状态)判断请求是否成功,若是成功的话,咱们将读取xhr.responseText
属性中存储的返回值。可是,当咱们的请求为异步时,问题就稍微变得复杂了,因为是异步的请求,在xhr.send(null)
语句被执行后,JavaScript引擎会紧接着执行下面的判断语句,而这时因为还没有来得及响应,咱们注定会获得一个默认的xhr.status值,所以,咱们永远都不可能获取请求的资源了。
如何解决这个问题?答案是经过为XMLHTTPRequest实例添加onreadystatechange
事件处理程序(固然你也能够直接使用DOM2级规范规定的.addEventListener()
方法,可是注意,IE8是不支持该方法的)。
xhr实例的readystatechange
事件会监听xhr.readyState
属性的变化,你能够将这个属性想象为一个计数器,随着AJAX流程的推动而不断累加,其可取的值以下:
.open()
方法;.open()
方法,但还没有调用.send()
方法;.send()
方法,但还没有接收到响应;有了这个时间处理程序对AJAX进程作监听,剩下的事就简单多了,一个异步的GET请求代码以下:
const xhr = new XMLHttpRequest()
xhr.onreadystatechange = () => {
if (xhr.readystate == 4) {
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {
alert(xhr.responseText)
} else {
alert("Request was unsuccessful: " + xhr.status)
}
}
}
xhr.open("get", "example.php", true)
xhr.send(null)
复制代码
注意:为了确保跨浏览器的兼容性,必需要在调用.open()
方法以前指定事件处理程序,仔细想一想也有道理,毕竟.open()
方法的执行也包含在该事件处理程序的监听范围以内对吧?
有时候,你可能须要在接收到响应以前取消异步请求,这时候,你须要调用.abort()
方法。
该方法会令XHR对象实例中止触发事件,而且再也不容许访问任何和响应有关的对象属性。没了监控器,咱们再也无法判断响应了不是吗?
可是须要注意的是,当终止AJAX请求后,你须要手动对XHR对象实例进行解绑以释放内存空间。
🎉🎉 恭喜你!到这里你已经学会了全部的AJAX基础知识,你知道了AJAX是什么,存在的意义以及如何真正发起一个AJAX请求并接收响应,你已是一个AJAX大师!祝贺你!太棒了!🎉🎉
🤜 真棒,尊敬的AJAX大师,你竟然尚未离开,那么我将传授你最后一部分AJAX秘籍,帮助你成为一个真正的AJAX忍者,这是你的坚持赢得的!
还记得咱们一开始有提到,W3C提出了XMLHttpRequest 2级规范吗?虽然并不是全部的浏览器都实现了该规范所规定的内容,但仍是有一些内容被所有或大多数浏览器所实现。想成为AJAX忍者?往下看吧。
提示:在这一部分,你将会看到不少有关浏览器兼容性的文字,但愿你不要以为枯燥,毕竟这但是忍者的修行,对吧?
FormData是XMLHttpRequest 2级为咱们提供的新的数据类型(构造函数),还记的咱们以前是如何假装一个POST请求为一个表单提交吗?FormData令这一过程变得更加轻松,由于XHR对象可以识别传入的数据类型是FormData的实例,并自动配置适当的头部信息。
FormData的使用方式以下:
// 添加数据
let data1 = new FormData()
data1.append("name", "Tom")
xhr.send(data1)
// 提取表单数据
let data2 = new FormData(document.forms[0])
xhr.send(data2)
复制代码
除此以外,FormData的另外一个好处是相较于传统的AJAX请求,它容许咱们上传二进制数据(图片,视频,音频等),具体详情可查看该连接。
FormData的浏览器兼容性:
当咱们发送一个AJAX请求,却迟迟得不到服务器响应,这种感受是很糟糕的。为了缓解这种糟糕的感受,XMLHttpRequest 2级规范为咱们提供了一个额外的属性和事件监听事件:
timeout
属性:设置超时时间,单位为毫秒;timeout
事件:当响应时间超出实例对象timeout属性时被触发;使用方式以下:
// 当响应时间超过1秒时,请求停止,弹出提示框
xhr.timeout = 1000
xhr.ontimeout = () => { alert("Request did not return in a second.") }
复制代码
注意,当请求终止时,会调用ontimeout
事件处理程序,此时xhr的readyState
属性的值可能已变为4,这意味着会继续调用onreadystatechange
事件处理程序,可是当超时停止请求后再访问xhr的status
属性会使浏览器抛出一个错误,所以须要将检查status
属性的语句放入try-catch
语句中。
虽然带来了一些麻烦,可是咱们却对XMLHttpRequest对象有了更多的控制。
浏览器兼容性:
响应返回的响应头里,描述了返回数据的MIME类型,浏览器经过识别该类型,告知XMLHttpRequest实例处理该数据的方式。然而有时候(例如将XML类型数据当作纯文本处理),咱们想要以咱们想要的方式处理响应的数据,在XMLHttpRequest 2级规范中,咱们可使用.overrideMimeType()
方法,从方法名也能够轻松猜出,该方法能够覆写响应头所描述数据的MIME类型。
其写法以下:
const xhr = new XMLHttpRequest()
xhr.open("get", "example.php", true)
xhr.overrideMimeType("text/xml") // 强迫浏览器将响应数据以指定类型方式解读
xhr.send(null)
复制代码
至此,咱们掌控了响应数据的处理方式。
浏览器兼容性:
Progress Events规范是W3C制定的一个工做草案。该规范定义了与客户端与服务器通讯相关的一系列事件,这些事件监听了通讯进程中的各个关键节点,使咱们可以以更细的颗粒度掌控数据传输过程当中的细节。目前共有6个进度事件,他们会随数据传输进展被顺序触发(除了error,abort事件),让咱们看看他们的定义和浏览器兼容状况:
loadstart
:在接收到响应数据的第一个字节时触发;
progress
:在接收响应期间持续不断地触发;
error
:在请求发生错误时触发;
abort
:再由于调用abort()
方法时触发;
load
:在接收到完整的响应数据时触发;
loadend
:在通讯完成或者触发error
,abort
或load
事件后触发;
这里咱们将着重展开讲解如下两个事件:
① load事件
该事件帮助咱们节省了readstatechange
事件,咱们没必要在XHR对象实例上绑定该事件监听函数以追踪实例上readState
属性的变化,而是能够直接使用如下代码:
const xhr = new XMLHttpRequest()
xhr.onload = () => {
if ((xhr.status >= 200 && xhr.status <300) || xhr.status == 304) {
alert(xhr.responseText)
} else {
alert("Something wrong!")
}
}
xhr.open("get", "example.php", true)
xhr.send(null)
复制代码
② progress事件
该事件令咱们能够实现咱们求之不得的加载进度条效果。由于onprogress
事件处理程序会接收到一个event
对象,其target
属性为XHR对象实例,但却额外包含着三个属性:
lengthComputable
:表示进度信息是否可用的布尔值;position
:表示目前接收的字节数;totalSize
:表示根据Content-Length响应头部肯定的预期字节数;很显然,咱们的加载进度条所需的一切资源都准备就绪,咱们只需写出下面的代码:
const xhr = new XMLHttpRequest()
xhr.onload = () => {
if ((xhr.status >= 200 && xhr.status <300) || xhr.status == 304) {
alert(xhr.responseText)
} else {
alert("Something wrong!")
}
}
// 加载进度条
xhr.onprogress = function(event) {
const divStatus = document.getElementById("status")
if (event.lengthComputable) {
divStatus.innerHTML = `Received ${event.postion} of ${event.totalSize} bytes`
}
}
xhr.open("get", "example.php", true)
xhr.send(null)
复制代码
一切大功告成!不过还要记得注意,须要在.open()
方法前调用onprogress
事件处理程序。
太棒了,关于AJAX,我已经没有什么可说的了,若是你已经掌握了以上全部概念,那么“AJAX忍者”的称号你当之无愧。
我真的为你感到骄傲,Great Work!🙌
👋 Hey!喜欢这篇文章吗?别忘了在下方👇 点赞让我知道。