Ajax
技术的核心是XMLHttpRequest
对象。咱们使用XMLHttpRequest
对象来发送一个Ajax
请求。这是由微软首先引入的一个特性,其余浏览器提供商后来都提供了相同的实现。javascript
XMLHttpRequest
已经获得普遍接受,后来W3C
对它进行了标准化,提出了XMLHttpRequest
标准。XMLHttpRequest
标准又分为Level 1
和Level 2
。html
并不是全部浏览器都完整地实现了XMLHttpRequest 2级
规范,但全部浏览器都实现了它规定的部份内容。html5
XMLHttpRequest Level 1
主要存在如下缺点:java
Level 2
对Level 1
进行了改进,XMLHttpRequest Level 2
中新增了如下功能:ajax
formData
对象,支持发送表单数据。下面的一行代码就能够建立XMLHttpRequest
对象。json
const xhr = new XMLHttpRequest()
复制代码
兼容性查询:caniuse.com/#search=XML…segmentfault
Accept
:客户端能够处理的内容类型。好比:Accept: */*
。Accept-Charset
:客户端能够处理的字符集类型。好比:Accept-Charset: utf8
。Accept-Encoding
:客户端能够处理的压缩编码。好比:Accept-Encoding: gzip, deflate, br
。Accept-Language
:客户端当前设置的语言。好比:Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
。Connection
:客服端与服务器之间链接的类型。好比:Connection: keep-alive
。Cookie
:当前页面设置的任何Cookie
。Host
:发出请求页面所在的域。Referer
:表示当前请求页面的来源页面的地址,即当前页面是经过此来源页面里的连接进入的。User-Agent
:客户端的用户代理字符串。通常包含浏览器、浏览器内核和操做系统的版本型号信息。Content-Type
:客户端告诉服务器实际发送的数据类型。好比:Content-Type: application/x-www-form-urlencoded
。open()
方法用于初始化一个请求。跨域
open()
方法接收三个参数:数组
method
:要发送的请求的类型。好比GET
、POST
、PUT
、DELETE
等。url
:请求的URL
。async
:是否异步发送请求的布尔值。true
为异步发送请求。const xhr = new XMLHttpRequest()
xhr.open('get', '/userInfo', true)
复制代码
调用open()
方法并不会真正发送请求,而只是启动一个请求以备发送。
send()
方法用于发送HTTP
请求。
send()
方法接收一个参数:
data
:做为请求主体发送的数据。若是不须要经过请求主体发送数据,则必须传入null
。该参数能够接收字符串、FormData
、Blob
、ArrayBuffer
等类型。const xhr = new XMLHttpRequest()
xhr.send(null)
复制代码
setRequestHeader()
方法能够设置自定义的请求头部信息。
setRequestHeader()
方法接收二个参数:
header
:头部字段的名称。value
:头部字段的值。要成功发送请求头部信息,此方法必须在open()
和send()
之间调用。
const xhr = new XMLHttpRequest()
xhr.open('get', '/server', true)
xhr.setRequestHeader('MyHeader', 'MyValue')
xmlhttp.send()
复制代码
readyState
属性表示请求/响应过程的当前活动阶段。这个属性的值以下:
UNSENT
)未初始化。还没有调用open()
方法。OPENED
)启动。已经调用open()
方法,但没有调用send()
方法。HEADERS_RECEIVED
)发送。已经调用send()
方法,但还没有接收到响应。LOADING
)接收。已经接收到部分响应数据。DONE
)完成。已经接收到所有响应数据。只要readyState
属性的值发生变化,都会触发一次onreadystatechange
事件。利用这个事件来检测每次状态变化后readyState
的值。通常状况下只对readyState
值为4
的阶段作处理,这时全部数据都已经就绪。
const xhr = new XMLHttpRequest()
xhr.open('get', '/server', true)
xhr.onreadystatechange = function () {
if(xhr.readyState !== 4) {
return
}
if(xhr.status >= 200 && xhr.status < 300) {
console.log(xhr.responseText)
}
}
xhr.send(null)
复制代码
timeout
属性表示请求在等待响应多少毫秒以后就终止。若是在规定的时间内浏览器尚未接收到响应,就会触发ontimeout
事件处理程序。
const xhr = new XMLHttpRequest()
xhr.open('get', '/server', true)
//将超时设置为3秒钟
xhr.timeout = 3000
// 请求超时后请求自动终止,会调用 ontimeout 事件处理程序
xhr.ontimeout = function(){
console.log('请求超时了')
}
xhr.send(null)
复制代码
overrideMimeType()
方法可以重写服务器返回的MIME
类型,从而让浏览器进行不同的处理。
假如服务器返回的数据类型是text/xml
,因为种种缘由浏览器解析不成功报错,这时就拿不到数据。为了拿到原始数据,咱们能够把MIME
类型改为text/plain
,这样浏览器就不会去自动解析,从而咱们就能够拿到原始文本了。
const xhr = new XMLHttpRequest()
xhr.open('get', '/server', true)
xhr.overrideMimeType('text/plain')
xhr.send(null)
复制代码
responseType
属性是一个字符串,表示服务器返回数据的类型。使用xhr.response
属性来接收。
这个属性是可写的,能够在调用open()
方法以后,send()
方法以前设置这个属性的值,告诉服务器返回指定类型的数据。若是responseType
设为空字符串,等同于默认值text
。
responseType
属性能够设置的格式类型以下:
responseType 属性的值 |
response 属性的数据类型 |
说明 |
---|---|---|
"" |
String 字符串 |
默认值,等同于text (在不设置responseType 时) |
"text" |
String 字符串 |
服务器返回文本数据 |
"document" |
Document 对象 |
但愿返回XML 格式数据时使用 |
"json" |
javaScript 对象 |
IE10/IE11 不支持 |
"blob" |
Blob 对象 |
服务器返回二进制对象 |
"arrayBuffer" |
ArrayBuffer 对象 |
服务器返回二进制数组 |
当将responseType
设置为一个特定的类型时,你须要确保服务器所返回的类型和你所设置的返回值类型是兼容的。那么若是二者类型不兼容,服务器返回的数据就会变成null
,即便服务器返回了数据。
给一个同步请求设置responseType
会抛出一个InvalidAccessError
的异常。
// 获取一张图片代码示例
const xhr = new XMLHttpRequest()
xhr.open('get', '/server/image.png', true)
xhr.responseType = 'blob'
xhr.onload = function(e) {
if (xhr.status >= 200 && xhr.status < 300) {
const blob = this.response
// ...
}
}
xhr.send(null)
复制代码
withCredentials
属性是一个布尔值,表示跨域请求时是否协带凭据信息(cookie
、HTTP
认证及客户端SSL
证实等)。默认为false
。
若是须要跨域Ajax
请求发送Cookie
,须要withCredentials
属性设为true
。若是在同域下配置xhr.withCredentials
,不管配置true
仍是false
,效果都会相同。
const xhr = new XMLHttpRequest()
xhr.open('get', '/server', true)
xhr.withCredentials = true
xhr.send(null)
复制代码
当配置了withCredentials
为true
时,必须在后端增长response
头信息Access-Control-Allow-Origin
,且必须指定域名,而不能指定为*
。还要添加Access-Control-Allow-Credentials
这个头信息为true。
response.addHeader("Access-Control-Allow-Origin", "http://example.com")
response.addHeader("Access-Control-Allow-Credentials", "true")
复制代码
在接收到响应以前调用abort()
方法用来取消异步请求。当一个请求被终止后,它的readyState
属性将被置为0
。在终止请求以后,还应该对XMLHttpRequeat
对象进行解引用操做。
当调用abort()
后,会触发onabort
事件。
const xhr = new XMLHttpRequest()
xhr.open('get', '/server', true)
xmlhttp.onabort = function () {
console.log('请求被停止')
}
xmlhttp.send()
// 将会调用咱们上面定义的 onabort 回调函数
xmlhttp.abort()
复制代码
将查询字符串参数追加到URL
的末尾,将信息发送给服务器。
GET
参数的编码方式是没法人为干涉的,这致使了不一样浏览器有不一样的编码方式,所以最稳妥的方案是人工预编码,人工解码,从而禁止浏览器编码的干涉。
const xhr = new XMLHttpRequest()
// 使用encodeURIComponent()进行编码
const tempParam = encodeURIComponent('age')
const tempValue = encodeURIComponent('20')
xhr.open('get', '/server?tempParam=tempValue&money=100', true)
复制代码
POST
请求把数据做为请求的主体(请求的body
)提交。下面是四种常见的POST
请求提交数据方式。
浏览器的原生<form>
表单,若是不设置enctype
属性,那么最终就会以application/x-www-form-urlencoded
方式提交数据。
表单上传文件时,必须让<form>
表单的enctype
等于multipart/form-data
。
当发送Ajax
请求时,把application/json
做为请求头,用来告诉服务端消息主体是序列化后的JSON
字符串。
使用HTTP
做为传输协议,XML
做为编码方式的远程调用规范。
将Content-Type
头部信息设置为application/x-www-form-urlencoded
。可使用XMLHttpRequest
对象来模仿表单提交。
const xhr = new XMLHttpRequest()
xhr.open('post', '/server', true)
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
const form = document.getElementById('myForm')
// serialize()为表单序列化方法
xhr.send(serialize(form))
复制代码
也可使用XMLHttpRequest level 2
的FormData
来序列化表单数据。
const xhr = new XMLHttpRequest()
xhr.open('post', '/server', true)
const form = document.getElementById('myForm')
const formData = new FormData(form)
formData.append("id", "123456")
xhr.send(formData)
复制代码
使用FormData
没必要明确地在XMLHttpRequest
对象上设置请求头部。XMLHttpRequest
对象可以识别传入的数据类型是FormData
的实例,并配置适当的头部信息。
在XMLHttpRequest
对象开始传送数据时被触发,也就是调用send()
方法(HTTP
请求发出)的时候。
xhr.onloadstart = function () {
console.log('开始发出请求...')
}
复制代码
在接收响应期间持续不断地触发。
onprogress
事件处理程序会接收到一个event
对象,它的target
属性是XMLHttpRequest
对象,而且event
包含着三个额外的属性:loaded
、total
和lengthComputable
。
event.loaded
:已传输的数据量(已经接收的字节数)。event.total
:总共的数据量(根据Content-Length
响应头部肯定的预期字节数)。event.lengthComputable
:进度信息是否可用的布尔值。有了这些信息,就能够建立一个Ajax
请求进度条了。
const xhr = new XMLHttpRequest()
xhr.onprogress = function (event) {
if (!event.lengthComputable) {
return console.log('没法计算进展')
}
const percentComplete = event.loaded / event.total * 100
console.log(`进度百分比:${percentComplete}%`)
}
xhr.open('post', '/server', true)
xhr.send(null)
复制代码
在请求发生错误时触发。只有发生了网络层级别的异常才会触发此事件,对于应用层级别的异常,好比响应返回的statusCode
是4xx
时,并不属于NetWork Error
,因此不会触发onerror
事件,而是会触发onload
事件。
xhr.onerror = function(e) {
console.log('数据接收出错')
}
复制代码
调用abort()
方法而终止请求时触发。
当请求成功,接收到完整的响应数据时触发。
可使用onload
事件能够用来替代readystatechange
事件。由于响应接收完毕后将触发onload
事件,所以也就没有必要去检查readyState
属性了。只要浏览器接收到服务器的响应,无论其状态如何,都会触发load
事件。
const xhr = new XMLHttpRequest()
xhr.onload = function onload() {
console.log('数据接收完毕')
if(xhr.status >= 200 && xhr.status < 300) {
console.log(xhr.responseText)
}
}
xhr.open('post', '/server', true)
xhr.send(formData)
复制代码
为确保正常执行,必须在调用open()
方法以前添加onprogress
事件处理程序。
在请求结束(包括请求成功和请求失败),或者触发error
、abort
或load
事件后触发。
xhr.onloadend = function(e) {
console.log('请求结束,状态未知')
}
复制代码
每一个请求都从触发
loadstart
事件开始,接下来是一或多个progress
事件,而后触发error
、abort
或load
事件中的一个,最后以触发loadend
事件结束。
XMLHttpRequest
不只能够发送请求,还能够发送文件,就是Ajax
文件上传。
发送文件之后,经过XMLHttpRequest.upload
属性能够获得一个XMLHttpRequestUpload
对象。经过这个对象,能够得知上传的进展。实现方案就是监听这个对象的各类事件:onloadstart
、onprogress
、onabort
、onerror
、onload
、ontimeout
、onloadend
。
当文件上传时,对upload
属性指定progress
事件的监听函数,可得到上传的进度。
const xhr = new XMLHttpRequest()
if (xhr.upload) {
xhr.upload.onprogress = function progress(e) {
if (e.total > 0) {
e.percent = e.loaded / e.total * 100
}
}
}
复制代码
在接收到响应后,第一步是检查status
属性。以肯定响应已经成功返回。将HTTP
状态代码为200
做为成功的标志。状态代码为304
表示请求的资源并无被修改,能够直接使用浏览器中缓存的版本,也被认为是有效的。
Content-Type
:服务器告诉客户端响应内容的类型和采用字符编码。好比:Content-Type: text/html; charset=utf-8
。Content-Length
:服务器告诉客户端响应实体的大小。好比:Content-Length: 8368
。Content-Encoding
:服务器告诉客户端返回的的压缩编码格式。好比:Content-Encoding: gzip, deflate, br
。status
属性返回一个整数,表示服务器回应的HTTP
状态码。若是服务器没有返回状态码,那么这个属性默认是200
。请求发出以前,该属性为0
。该属性只读。
if (xhr.readyState === 4) {
if (xhr.status >= 200 && xhr.status < 300) {
// 处理服务器的返回数据
}
}
复制代码
statusText
属性返回一个字符串,表示服务器发送的状态说明。好比OK
和Not Found
。在请求发送以前,该属性的值是空字符串。若是服务器没有返回状态提示,该属性的值默认为OK
。该属性为只读属性。
要经过检测status
属性来决定下一步的操做,不要依赖statusText
,由于statusText
在跨浏览器使用时不太可靠。
response
属性表示服务器返回的数据。它能够是任何数据类型,好比字符串、对象、二进制对象等等,具体的类型由XMLHttpRequest.responseType
属性决定。该属性只读。
若是本次请求没有成功或者数据不完整,该属性等于null
。
const xhr = new XMLHttpRequest()
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
console.log(xhr.response)
}
}
复制代码
responseText
属性返回从服务器接收到的字符串,该属性为只读。
if (xhr.readyState === 4) {
if (xhr.status >= 200 && xhr.status < 300) {
// 处理服务器的返回数据
console.log(xhr.responseText)
}
}
复制代码
若是响应的内容类型是"text/xml"
或"application/xml"
,这个属性中将保存包含着响应数据的HTML
或XML
文档对象。该属性是只读属性。
不管内容类型是什么,响应主体的内容都会保存到responseText属性中。而对于非XML数据而言,responseXML属性的值将为null。
responseURL
属性是字符串,表示发送数据的服务器的网址。若是URL
为空则返回空字符串。若是URL
有锚点,则位于URL#
后面的内容会被删除。若是服务器端发生跳转,这个属性返回最后实际返回数据的网址。
const xhr = new XMLHttpRequest()
xhr.open('GET', 'http://example.com/test', true)
xhr.onload = function () {
// 返回 http://example.com/test
console.log(xhr.responseURL)
}
xhr.send(null)
复制代码
getResponseHeader()
方法返回HTTP
头信息指定字段的值,若是尚未收到服务器的响应或者指定字段不存在,返回null
。该方法的参数不区分大小写。
const xhr = new XMLHttpRequest()
xhr.onload = function onload() {
console.log(xhr.getResponseHeader('Content-Type'))
}
xhr.open('post', '/server', true)
xhr.send(null)
复制代码
若是有多个字段同名,它们的值会被链接为一个字符串,每一个字段之间使用逗号+空格分隔。
getAllResponseHeaders()
方法返回一个字符串,表示服务器发来的全部HTTP
头信息。格式为字符串,每一个头信息之间使用CRLF
分隔(回车+换行),若是没有收到服务器回应,该属性为null
。若是发生网络错误,该属性为空字符串。
const xhr = new XMLHttpRequest()
xhr.onload = function onload() {
const responseHeaders = 'getAllResponseHeaders' in xhr ? xhr.getResponseHeaders() : null
}
xhr.open('post', '/server', true)
xhr.send(null)
复制代码
上面代码用于获取服务器返回的全部头信息。返回值多是下面这样的字符串。
content-encoding: gzip\r\n
content-length: 2020\r\n
content-type: text/html; charset=utf-8\r\n
复制代码
须要对这个字符串进行处理才能正确使用。
const str = 'date: Fri, 08 Dec 2017 21:04:30 GMT\r\n'
+ 'content-encoding: gzip\r\n'
function trim(str) {
return str.replace(/^\s*/, '').replace(/\s*$/, '')
}
function parseHeaders(headers) {
if (!headers) {
return {}
}
const parsed = {}
let key, val, i
const arr = headers.split(/[\r\n]+/)
arr.forEach((line) => {
i = line.indexOf(':')
key = trim(line.substr(0, i)).toLowerCase()
val = trim(line.substr(i + 1))
if (key) {
parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val
}
})
return parsed
}
//{date: "Fri, 08 Dec 2017 21:04:30 GMT", content-encoding: "gzip"}
console.log(parseHeaders(str))
复制代码