XHR 简介

XMLHttpRequest(XHR)对象用于与服务器交互。通过 XMLHttpRequest 可以在不刷新页面的情况下请求特定 URL,获取数据。这允许网页在不影响用户操作的情况下,更新页面的局部内容。XMLHttpRequestAJAX 编程中被大量使用。

基本使用

通过 XMLHttpRequest 构造函数可以初始化一个 XMLHttpRequest 实例对象。

  1. // 1.创建实例
  2. const xhr = new XMLHttpRequest()
  3. // 2.初始化一个请求(请求类型和 URL)
  4. xhr.open('GET', 'http://127.0.0.1:8000/server')
  5. // 3.发送请求
  6. xhr.send()
  7. // 4.绑定事件,处理服务器端返回结果
  8. xhr.onreadystateChange = function () {
  9. // 判断当前服务端返回了所有结果
  10. if (xhr.readState === 4) {
  11. // 判断响应成功时(2 开头的状态都是成功的)
  12. if (xhr.status >= 200 && xhr.status < 300) {
  13. // 处理结果
  14. } else {
  15. // ...
  16. }
  17. }
  18. }

属性

属性 描述
onreadystatechange 接受一个回调函数作为值,当 readystate 属性发生改变时调用函数。
readyState 存有 XMLHttpRequest 的状态。从 0 到 4 发生变化。
status 返回请求的响应状态(例如,”200”、”404”)。
statusText 返回响应状态字符串(例如,”OK”、”Not Found”)。
response 返回整个响应体,具体是哪种类型取决于 responseType 属性。
responseText 只能返回”text”类型的响应。
responseType 返回响应数据的类型。它允许我们手动设置返回数据的类型。如果我们将它设置为一个空字符串,它将使用默认的”text”类型。
timeout 表示该请求的最大请求时间(毫秒),若超出该时间,请求会自动终止。
ontimeout 接受一个回调函数作为值,当请求超时时调用函数。
onerror 接受一个回调函数作为值,当请求遭遇错误时调用函数。

方法

方法 描述
open(method,url,async) 规定请求的类型、URL 以及是否异步处理请求。

- method:请求的类型;GET 或 POST
- url:文件在服务器上的位置
- async:true(异步)或 false(同步)
send(string) 将请求发送到服务器。

- string:设置请求体,仅用于 POST 请求
setRequestHeader(header,value) 向请求添加 HTTP 头。

- header: 规定头的名称
- value: 规定头的值
getAllResponseHeaders 以字符串的形式返回所有响应头数据,如果没有收到响应,则返回 null。
abort 如果请求已被发出,则立刻中止请求。并将请求的 status 置为 0。

事件

事件 描述
error 当 request 遭遇错误时触发,也可以使用 onerror 属性。
timeout 在预设时间内没有接收到响应时触发,也可以使用 ontimeout 属性。

示例:

  1. const xhr = new XMLHttpRequest();
  2. xhr.addEventListener('error', handleEvent);
  3. xhr.addEventListener('timeout', handleEvent);

readyState 状态码

状态 描述
0 UNSENT 代理被创建,但尚未调用 open() 方法。
1 OPENED open() 方法已经被调用。
2 HEADERS_RECEIVED send() 方法已经被调用,并且头部和状态已经可获得。
3 LOADING 下载中; responseText 属性已经包含部分数据。
4 DONE 下载操作已完成。

GET 设置请求参数

例如 https://www.baidu.com/s?wd=HTTP,问号(?)后面的就是请求参数。可以通过 open 方法设置。

  1. const xhr = new XMLHttpRequest()
  2. // 设置请求参数
  3. xhr.open('GET', 'http://127.0.0.1:8000/server?a=100&b=300')

image.png

POST 设置请求体

POST 请求体需要在 send 方法中设置。

  1. const xhr = new XMLHttpRequest()
  2. xhr.open('POST', 'http://127.0.0.1:8000/server')
  3. // 设置请求体
  4. xhr.send('a=100&b=200')

请求体可以是任意格式,只要服务器能够处理。

image.png

设置请求头信息

通过 setRequestHeader 方法设置请求头信息,该方法接收两个参数,第一个参数是属性的名称,第二个参数是属性的值。必须在 open() 之后、send() 之前调用。

  1. const xhr = new XMLHttpRequest()
  2. xhr.open('GET', 'http://127.0.0.1:8000/server')
  3. xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
  4. xhr.send()

image.png

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

服务端响应 JSON 数据

node 的 send 方法只能发送字符串或者 buffer 类型的数据,在实际应用中,服务器响应的绝大多数情况都是 JSON 格式的数据,所以要对响应的数据做一个处理。

第一种方式可以使用 JSON.parse 方法进行手动转换

  1. const xhr = new XMLHttpRequest()
  2. xhr.open("POST", "http://localhost:8000/json-server")
  3. xhr.send()
  4. xhr.onreadystatechange = function () {
  5. if (xhr.readyState === 4) {
  6. if (xhr.status >= 200 && xhr.status < 300) {
  7. // 将 JSON 转换成对象
  8. let data = JSON.parse(xhr.response)
  9. console.log(data) // {name: "zs"}
  10. }
  11. }
  12. }

第二种方法可以通过 XMLHttpRequest.responseType 属性设置响应类型,如果值为 'json' 将会自动进行转换。

  1. const xhr = new XMLHttpRequest()
  2. // 设置响应类型
  3. xhr.responseType = 'json'
  4. xhr.open("POST", "http://localhost:8000/json-server")
  5. xhr.send()
  6. xhr.onreadystatechange = function () {
  7. if (xhr.readyState === 4) {
  8. if (xhr.status >= 200 && xhr.status < 300) {
  9. console.log(xhr.response) // {name: "zs"}
  10. }
  11. }
  12. }

IE 缓存问题

IE 浏览器会对同一个 ajax 请求结果做一个缓存,这样会导致重新请求时,浏览器依然使用的是缓存内容。

既然是对同一个 ajax 请求才会缓存,那就发送不同的请求就能解决这个问题了:

  1. const xhr = new XMLHttpRequest()
  2. xhr.open("GET", "http://localhost:8000/ie?t=" + Date.now())
  3. xhr.send()

在请求 URL 后面加上一个动态参数就可以了。

请求超时与异常处理

我们不能保证服务端永远能够及时快速响应请求,为了给用户友好地反馈信息,需要设置请求超时的处理和请求异常时的处理。

  1. const xhr = new XMLHttpRequest()
  2. // 超时设置 2s
  3. xhr.timeout = 2000
  4. // 超时回调
  5. xhr.ontimeout = function () {
  6. alert("网络异常,请稍后重试!")
  7. }
  8. // 网络异常回调
  9. xhr.onerror = function () {
  10. alert("你的网络似乎出了一些问题!")
  11. }
  12. xhr.open("GET", "http://localhost:8000/delay")
  13. xhr.send()

请求的状态为 canceled 时,表示请求已经取消了,状态码为 0。

image.png

取消请求

在请求的过程中,当结果还没有响应时,我们可以使用 abort 方法手动取消这个请求。

  1. let flag = false
  2. const xhr = new XMLHttpRequest()
  3. xhr.open("GET", "http://localhost:8000/delay")
  4. xhr.send()
  5. if (!flag) {
  6. // 取消请求
  7. xhr.abort()
  8. }

请求重复问题

如果用户频繁去发送相同请求,服务器的压力会很大。我们可以在用户发送请求的时候,判断是否发送过相同的请求,如果有,则可以取消请求并重新发送一个新的请求。

  1. // 用来存放 XMLHttpRequest 实例
  2. let xhr = null
  3. // 标识变量,是否发送请求
  4. let isSending = false
  5. btn.addEventListener("click", function () {
  6. // 如果已经发送了则取消请求
  7. if (isSending) xhr.abort()
  8. // 当实例被创建时,表示正在发送请求
  9. xhr = new XMLHttpRequest()
  10. // 此时更改标识变量
  11. isSending = true
  12. xhr.open("GET", "http://localhost:8000/delay")
  13. xhr.send()
  14. xhr.onreadystatechange = function () {
  15. if (xhr.readyState === 4) {
  16. // 当请求操作完成之后,还原标识变量
  17. isSending = false
  18. }
  19. }
  20. })