基础篇 05 AJAX
AJAX 是异步 JavaScript 和 XML(Asynchronous JavaScript And XML),指的是通过 JavaScript 的异步通信,从服务器获取 XML 文档并从中提取数据,再更新当前网页的对应部分(不用刷新整个网页)。AJAX 本身不是一种新技术,而是一个在 2005 年被 Jesse James Garrett 提出的新术语,用来描述一种使用现有技术集合的新方法,包括:HTML 或 XHTML、CSS、JavaScript、DOM、XML、XSLT 以及最重要的 XMLHttpRequest。
当使用结合了这些技术的 AJAX 模型以后, 网页应用能够快速地将增量更新呈现在用户界面上,而不需要重载(刷新)整个页面。这使得程序能够更快地回应用户的操作。
尽管 X 在 AJAX 中代表 XML,但由于 JSON 格式的许多优势,比如更加轻量以及作为 JavaScript 的一部分,目前 JSON 的使用比 XML 更加普遍。JSON 和 XML 都被用于在 AJAX 模型中打包信息。
1999 年,微软公司发布 IE 浏览器 5.0 版,第一次引入新功能:允许 JavaScript 脚本向服务器发起 HTTP 请求。这个功能当时并没有引起注意,直到 2004 年 Gmail 发布和 2005 年 Google Map 发布,才引起广泛重视。2005 年 2 月,AJAX 这个词第一次正式提出。
后来,AJAX 这个词就成为 JavaScript 脚本发起 HTTP 通信的代名词,也就是说,只要用脚本发起通信,就可以叫做 AJAX 通信。W3C 也在 2006 年发布了它的国际标准。
具体来说,AJAX 包括以下几个步骤:
创建 XMLHttpRequest 实例
发出 HTTP 请求
接收服务器传回的数据
更新网页数据
概括起来,就是一句话,AJAX 通过原生的 XMLHttpRequest 对象发出 HTTP 请求,得到服务器返回的数据后,再进行处理。现在,服务器返回的都是 JSON 格式的数据,XML 格式已经过时了,但是 AJAX 这个名字已经成了一个通用名词,字面含义已经消失了。
常用的 AJAX 库有:XMLHttpRequest、Node HTTP、fetch、node-fetch、axios。。。
XMLHTTP
浏览器与服务器之间,采用 HTTP 协议通信。用户在浏览器地址栏键入一个网址,或者通过网页表单向服务器提交内容,这时浏览器就会向服务器发出 HTTP 请求。
XMLHTTP 是一组 API 函数集,可被 JavaScript、JScript、VBScript 以及其它 Web 浏览器内嵌的脚本语言调用,通过 HTTP 在浏览器和 web 服务器之间收发 XML或其它数据。
XMLHTTP 最大的好处在于可以动态地更新网页,它无需重新从服务器读取整个网页,也不需要安装额外的插件。XMLHTTP 是 AJAX 网页开发技术的重要组成部分。除 XML 之外,XMLHTTP 还能用于获取其它格式的数据,如 JSON 或者甚至纯文本。
XMLHttpRequest
XMLHttpRequest(XHR)对象用于客户端与服务器之间的通信。通过 XMLHttpRequest 可以在不刷新页面的情况下请求特定 URL,获取数据。这允许网页在不影响用户操作的情况下,更新页面的局部内容。XMLHttpRequest 在 AJAX 编程中被大量使用。
XMLHttpRequest 对象是 AJAX 的主要接口。尽管名称有 XML 和 Http,实际上,XMLHttpRequest 可以用于获取任何类型的数据(包括字符串和二进制),而不仅仅是 XML。它甚至支持 HTTP 以外的协议(包括 file:// 和 FTP),但可能受到客户端出于安全等原因的限制。
XMLHttpRequest 本身是一个构造函数,可以使用 new 命令生成实例。它没有任何参数。
下面是 XMLHttpRequest 对象简单用法的完整例子:
XMLHttpRequest的实例属性
1.readyState
返回一个整数,表示实例对象的当前状态。该属性只读。它可能返回以下值:
0:表示 XMLHttpRequest 实例已经生成,但是实例的 open() 方法还没有被调用。
1:表示 open() 方法已经调用,但是实例的 send() 方法还没有调用,仍然可以使用实例的 setRequestHeader() 方法,设定 HTTP 请求的头信息。
2:表示实例的 send() 方法已经调用,并且服务器返回的头信息和状态码已经收到。
3:表示正在接收服务器传来的数据体(body 部分)。这时,如果实例的 responseType 属性等于 text 或者空字符串,responseText 属性就会包含已经收到的部分信息。
4:表示服务器返回的数据已经完全接收,或者本次接收已经失败。
通信过程中,每当实例对象发生状态变化,它的 readyState 属性的值就会改变。这个值每一次变化,都会触发 readyStateChange 事件。
2.onreadystatechange
指向一个监听函数。readystatechange 事件发生时(实例的 readyState 属性变化),就会执行这个属性。另外,如果使用实例的 abort() 方法,终止 XMLHttpRequest 请求,也会造成 readyState 属性变化。
3.response
一个只读属性,表示服务器返回的数据体(即 HTTP 回应的 body 部分)。它可能是任何数据类型,比如字符串、对象、二进制对象等等,具体的类型由 responseType 属性决定。
如果本次请求没有成功或者数据不完整,该属性等于 null。但是,如果 responseType 属性等于 text 或空字符串,在请求没有结束之前(readyState 等于 3 的阶段),response 属性包含服务器已经返回的部分数据。
4.responseType
该属性是一个字符串,表示服务器返回数据的类型。这个属性是可写的,可以在调用 open() 方法之后、调用 send() 方法之前,设置这个属性的值,告诉服务器返回指定类型的数据。
可以取以下值:
”“(空字符串):等同于 text,表示服务器返回文本数据。
“arraybuffer”:ArrayBuffer 对象,表示服务器返回二进制数组。
“blob”:Blob 对象,表示服务器返回二进制对象。
“document”:Document 对象,表示服务器返回一个文档对象。
“json”:JSON 对象。
“text”:字符串。
上面几种类型之中,text 类型适合大多数情况,而且直接处理文本也比较方便;Document 类型适合返回 HTML / XML 文档的情况,这意味着,对于那些打开 CORS 的网站,可以直接用 AJAX 抓取网页,然后不用解析 HTML 字符串,直接对抓取回来的数据进行 DOM 操作;blob 类型适合读取二进制数据,比如图片文件。
5.responseText
返回从服务器接收到的字符串,该属性为只读。只有 HTTP 请求完成接收以后,该属性才会包含完整的数据。
6.responseXML
返回从服务器接收到的 HTML 或 XML 文档对象,该属性为只读。如果本次请求没有成功,或者收到的数据不能被解析为 XML 或 HTML,该属性等于 null。
该属性生效的前提是 HTTP 回应的 Content-Type 头信息等于 text/xml
或 application/xml
。这要求在发送请求前,responseType 属性要设为 document。如果 HTTP 回应的 Content-Type 头信息不等于 text/xml
和 application/xml
,但是想从 responseXML 拿到数据(即把数据按照 DOM 格式解析),那么需要手动调用 overrideMimeType() 方法,强制进行 XML 解析。
该属性得到的数据,是直接解析后的文档 DOM 树。
7.responseURL
该属性是字符串,表示发送数据的服务器的网址。
注意: 这个属性的值与 open() 方法指定的请求网址不一定相同。如果服务器端发生跳转,这个属性返回最后实际返回数据的网址。另外,如果原始 URL 包括锚点,该属性会把锚点剥离。
8.status
一个只读属性,返回一个整数,表示服务器回应的 HTTP 状态码。一般来说,如果通信成功的话,这个状态码是200;如果服务器没有返回状态码,那么这个属性默认是200。请求发出之前,该属性为 0 。
200:OK,访问正常
301:Moved Permanently,永久移动
302:Move temporarily,暂时移动
304:Not Modified,未修改
307:Temporary Redirect,暂时重定向
401:Unauthorized,未授权
403:Forbidden,禁止访问
404:Not Found,未发现指定网址
500:Internal Server Error,服务器发生错误
9.statusText
一个只读属性,返回一个字符串,表示服务器发送的状态提示。不同于status
属性,该属性包含整个状态信息,比如 OK
和 Not Found
。在请求发送之前(即调用 open() 方法之前),该属性的值是空字符串;如果服务器没有返回状态提示,该属性的值默认为 OK
。
10.timeout
返回一个整数,表示多少毫秒后,如果请求仍然没有得到结果,就会自动终止。如果该属性等于0,就表示没有时间限制。
11.ontimeout
用于设置一个监听函数,如果发生 timeout 事件,就会执行这个监听函数。
12.事件监听属性
XMLHttpRequest 对象可以对以下事件指定监听函数。
onloadstart:loadstart 事件(HTTP 请求发出)的监听函数
onprogress:progress事件(正在发送和加载数据)的监听函数
onabort:abort 事件(请求中止,比如用户调用了abort()方法)的监听函数
onerror:error 事件(请求失败)的监听函数
onload:load 事件(请求成功完成)的监听函数
ontimeout:timeout 事件(用户指定的时限超过了,请求还未完成)的监听函数
onloadend:loadend 事件(请求完成,不管成功或失败)的监听函数
progress 事件的监听函数有一个事件对象参数,该对象有三个属性:
loaded:返回已经传输的数据量
total:属性返回总的数据量
lengthComputable:属性返回一个布尔值,表示加载的进度是否可以计算。
所有这些监听函数里面,只有 progress 事件的监听函数有参数,其他函数都没有参数。
**注意:**如果发生网络错误(比如服务器无法连通),onerror 事件无法获取报错信息。
13.withCredentials
该属性是一个布尔值,表示跨域请求时,用户信息(比如 Cookie 和认证的 HTTP 头信息)是否会包含在请求之中,默认为 false ,即向 example.com
发出跨域请求时,不会发送 example.com
设置在本机上的 Cookie(如果有的话)。
如果需要跨域 AJAX 请求发送 Cookie,需要 withCredentials 属性设为 true。注意,同源的请求不需要设置这个属性。
为了让这个属性生效,服务器必须显式返回 Access-Control-Allow-Credentials
这个头信息。
withCredentials 属性打开的话,跨域请求不仅会发送 Cookie,还会设置远程主机指定的 Cookie。反之,如果 withCredentials 属性没有打开,那么跨域的 AJAX 请求即使明确要求浏览器设置 Cookie,浏览器也会忽略。
注意: 脚本总是遵守同源政策,无法从 document.cookie
或者 HTTP 回应的头信息之中,读取跨域的 Cookie, withCredentials 属性不影响这一点。
14.upload
XMLHttpRequest 不仅可以发送请求,还可以发送文件,这就是 AJAX 文件上传。发送文件以后,通过 upload 属性可以得到一个对象,通过观察这个对象,可以得知上传的进展。主要方法就是监听这个对象的各种事件:loadstart、loadend、load、abort、error、progress、timeout。
当请求一切正常时,相关的事件触发顺序如下:
XMLHttpRequest的实例方法
1.open()
用于指定 HTTP 请求的参数,或者说初始化 XMLHttpRequest 实例对象。它一共可以接受五个参数:
method:表示 HTTP 动词方法,比如 GET、POST、PUT、DELETE、HEAD 等。
url:表示请求发送目标 URL。
async:布尔值,表示请求是否为异步,默认为 true。如果设为 false,则 send() 方法只有等到收到服务器返回了结果,才会进行下一步操作。该参数可选。由于同步 AJAX 请求会造成浏览器失去响应,许多浏览器已经禁止在主线程使用,只允许 Worker 里面使用。所以,这个参数不应该轻易设为 false。
user:表示用于认证的用户名,默认为空字符串。该参数可选。
password:表示用于认证的密码,默认为空字符串。该参数可选。
注意: 如果对使用过 open() 方法的 AJAX 请求,再次使用这个方法,等同于调用 abort(),即终止请求。
2.send()
用于实际发出 HTTP 请求。它的参数是可选的,如果不带参数,就表示 HTTP 请求只包含头信息,也就是只有一个 URL,典型例子就是 GET 请求;如果带有参数,就表示除了头信息,还带有包含具体数据的信息体,典型例子就是 POST 请求。
其参数技多种格式:ArrayBufferView、Blob、Document、String、FormData。
注意: 所有 XMLHttpRequest 的监听事件,都必须在 send() 方法调用之前设定。
3.setRequestHeader()
用于设置浏览器发送的 HTTP 请求的头信息。该方法必须在 open() 之后、send() 之前调用。如果该方法多次调用,设定同一个字段,则每一次调用的值会被合并成一个单一的值发送。
4.overrideMimeType()
用来指定 MIME 类型,覆盖服务器返回的真正的 MIME 类型,从而让浏览器进行不一样的处理。举例来说,服务器返回的数据类型是 text/xml
,由于种种原因浏览器解析不成功报错,这时就拿不到数据了。为了拿到原始数据,我们可以把 MIME 类型改成 text/plain
,这样浏览器就不会去自动解析,从而我们就可以拿到原始文本了。
注意: 该方法必须在 send() 方法之前调用。
5.getResponseHeader()
返回 HTTP 头信息指定字段的值,如果还没有收到服务器回应或者指定字段不存在,返回 null。该方法的参数不区分大小写。
如果有多个字段同名,它们的值会被连接为一个字符串,每个字段之间使用 “逗号+空格” 分隔。
6.getAllResponseHeaders()
返回一个字符串,表示服务器发来的所有 HTTP 头信息。格式为字符串,每个头信息之间使用 CRLF
分隔(回车+换行),如果没有收到服务器回应,该属性为 null。如果发生网络错误,该属性为空字符串。
7.abort()
用来终止已经发出的 HTTP 请求。调用这个方法以后,readyState 属性变为 4,status 属性变为 0。
XMLHttpRequest 实例的事件
更多现代浏览器,包括 Firefox,除了可以设置 on* 属性外,也提供标准的监听器 addEventListener() API 来监听 XMLHttpRequest 事件。
1.readyStateChange 事件
readyState 属性的值发生改变,就会触发 readyStateChange 事件。
可以通过 onReadyStateChange 属性,指定这个事件的监听函数,对不同状态进行不同处理。尤其是当状态变为 4 的时候,表示通信成功,这时回调函数就可以处理服务器传送回来的数据。
2.progress 事件
上传文件时,XMLHTTPRequest 实例对象本身和实例的 upload 属性,都有一个 progress 事件,会不断返回上传的进度。
3.load 事件、error 事件、abort 事件、loadend 事件
load 事件:表示服务器传来的数据接收完毕
error 事件:表示请求出错
abort 事件:表示请求被中断(比如用户取消请求)
loadend 事件:abort、load、error 这三个事件,会伴随一个 loadend 事件,表示请求结束,但不知道其是否成功。
4.timeout 事件
服务器超过指定时间还没有返回结果,就会触发 timeout 事件。
常见问题
AJAX 和 XMLHttpRequest 的正确理解
AJAX 是 asynchronous javascript and XML 的简写,中文翻译是异步的 javascript 和 XML,这一技术能够向服务器请求额外的数据而无须卸载页面,会带来更好的用户体验。它是一种技术方案,但并不是一种新技术。
XMLHttpRequest(XHR)是可用于 Web 浏览器脚本语言(例如 JavaScript)的 API。它用于将 HTTP 或 HTTPS 请求发送到 Web 服务器,并将服务器响应数据加载回脚本中。
就是一句话,ajax 通过原生的 XMLHttpRequest 对象发出HTTP请求,得到服务器返回的数据后,再进行处理。
XMLHttpRequest、axios、fetch 有什么区别?
XMLHttpRequest、axios、fetch 都是常用的 AJAX 库。
XMLHttpRequest
XMLHttpRequest 是最早的用于浏览器与服务器交换数据的方式。它可以使用 JSON、XML、HTML 和 text 文本等格式发送和接收数据,并且允许网页在不影响用户操作的情况下,更新页面的局部内容。
Axios
Axios 是一个基于 Promise 网络请求库,作用于 Node.js 和浏览器中。 它是 isomorphic 的(即同一套代码可以运行在浏览器和 Node.js 中)。在服务端它使用原生 Node.js http 模块,而在客户端则使用 XMLHttpRequest。
就客户端而言,它是基于 XHR 进行二次封装形成的工具库,主要特性有:
从浏览器创建 XMLHttpRequests
支持 Promise API
拦截、转换请求和响应
取消请求
自动转换 JSON 数据
客户端支持防御 XSRF
Fetch
Fetch 提供了一种合理的方式来跨网络异步获取资源(包括跨域请求)。
Fetch 是一个现代的概念,提供了许多与 XMLHttpRequest 相同的功能,但被设计成更具可扩展性和高效性。Fetch 提供了一个更理想的替代方案,可以很容易地被其他技术使用,例如 Service Workers。
fetch 主要特性:
fetch 是基于 Promise 的:无论请求成功与否,它都返回一个 Promise 对象,resolve 对应请求的 Response。该 promise 不会拒绝 Http 的错误状态,即使响应是一个 Http 404 或者 500。
fetch 没有设置超时时间。
fetch 没有办法原生监测请求的进度。
fetch 默认不带 cookie:除非使用了 credentials 的初始化选项。credentials 可选值:include(始终发送用户凭据,包括跨域)、same-origin(同源,则发送用户凭据)、omit(永远不要发送或接收 cookie)。用户凭据是指 cookie、基本 http 身份验证等。
一句话概括: XMLHttpRequest 是最早用于客户端与服务器交互数据的方式,是 W3C 标准;Fetch 可以认为是 XMLHttpRequest 的升级版,也是 W3C 标准,但兼容性稍差;Axios 是对 Node.js http 模块(用于 Node.js)和 XMLHttpRequest(用于浏览器)的封装。
响应头取Date异常
获取请求响应中的Date对象: res.request.getResponseHeader("Date")
,在浏览器中报错 Refused to get unsafe header "Date"
。
原因:默认的请求上, 浏览器只能访问以下默认的响应头
Cache-Control
Content-Language
Content-Type
Expires
Last-Modified
Pragma
解决:想让浏览器能访问到其他的 响应头的话 需要在服务器上设置 Access-Control-Expose-Headers
。比如想获取时间 Access-Control-Expose-Headers : 'Date'
。
常用的几种 Content-Type ?
application/x-www-form-urlencoded: 浏览器的原生 form 表单,如果不设置 enctype 属性,那么最终就会以
application/x-www-form-urlencoded
方式提交数据。该种方式提交的数据放在 body 里面,数据按照key1=val1&key2=val2
的方式进行编码,key 和 val 都进行了 URL 转码。multipart/form-data: 该种方式也是一个常见的 POST 提交方式,通常表单上传文件时使用该种方式。
application/json: 告诉服务器消息主体是序列化后的 JSON 字符串。
text/xml: 该种方式主要用来提交 XML 格式的数据。
客户端(JavaScript)下载服务端返回的文件流
特别注意: 请求参数一定要带上 responseType: 'blob'
。这是请求参数,不是请求头部的参数(即,不是在 Header 中)。
参考链接
最后更新于