XHR 简介
XMLHttpRequest(XHR)对象用于与服务器交互。通过 XMLHttpRequest 可以在不刷新页面的情况下请求特定 URL,获取数据。这允许网页在不影响用户操作的情况下,更新页面的局部内容。XMLHttpRequest 在 AJAX 编程中被大量使用。
基本使用
通过 XMLHttpRequest 构造函数可以初始化一个 XMLHttpRequest 实例对象。
// 1.创建实例const xhr = new XMLHttpRequest()// 2.初始化一个请求(请求类型和 URL)xhr.open('GET', 'http://127.0.0.1:8000/server')// 3.发送请求xhr.send()// 4.绑定事件,处理服务器端返回结果xhr.onreadystateChange = function () {// 判断当前服务端返回了所有结果if (xhr.readState === 4) {// 判断响应成功时(2 开头的状态都是成功的)if (xhr.status >= 200 && xhr.status < 300) {// 处理结果} else {// ...}}}
属性
| 属性 | 描述 |
|---|---|
| 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 属性。 |
示例:
const xhr = new XMLHttpRequest();xhr.addEventListener('error', handleEvent);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 方法设置。
const xhr = new XMLHttpRequest()// 设置请求参数xhr.open('GET', 'http://127.0.0.1:8000/server?a=100&b=300')

POST 设置请求体
POST 请求体需要在 send 方法中设置。
const xhr = new XMLHttpRequest()xhr.open('POST', 'http://127.0.0.1:8000/server')// 设置请求体xhr.send('a=100&b=200')
请求体可以是任意格式,只要服务器能够处理。

设置请求头信息
通过 setRequestHeader 方法设置请求头信息,该方法接收两个参数,第一个参数是属性的名称,第二个参数是属性的值。必须在 open() 之后、send() 之前调用。
const xhr = new XMLHttpRequest()xhr.open('GET', 'http://127.0.0.1:8000/server')xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')xhr.send()

注意:自定义一些 header 属性进行跨域请求时,可能会遇到”not allowed by Access-Control-Allow-Headers in preflight response“,你可能需要在你的服务端设置”Access-Control-Allow-Headers”。
服务端响应 JSON 数据
node 的 send 方法只能发送字符串或者 buffer 类型的数据,在实际应用中,服务器响应的绝大多数情况都是 JSON 格式的数据,所以要对响应的数据做一个处理。
第一种方式可以使用 JSON.parse 方法进行手动转换
const xhr = new XMLHttpRequest()xhr.open("POST", "http://localhost:8000/json-server")xhr.send()xhr.onreadystatechange = function () {if (xhr.readyState === 4) {if (xhr.status >= 200 && xhr.status < 300) {// 将 JSON 转换成对象let data = JSON.parse(xhr.response)console.log(data) // {name: "zs"}}}}
第二种方法可以通过 XMLHttpRequest.responseType 属性设置响应类型,如果值为 'json' 将会自动进行转换。
const xhr = new XMLHttpRequest()// 设置响应类型xhr.responseType = 'json'xhr.open("POST", "http://localhost:8000/json-server")xhr.send()xhr.onreadystatechange = function () {if (xhr.readyState === 4) {if (xhr.status >= 200 && xhr.status < 300) {console.log(xhr.response) // {name: "zs"}}}}
IE 缓存问题
IE 浏览器会对同一个 ajax 请求结果做一个缓存,这样会导致重新请求时,浏览器依然使用的是缓存内容。
既然是对同一个 ajax 请求才会缓存,那就发送不同的请求就能解决这个问题了:
const xhr = new XMLHttpRequest()xhr.open("GET", "http://localhost:8000/ie?t=" + Date.now())xhr.send()
在请求 URL 后面加上一个动态参数就可以了。
请求超时与异常处理
我们不能保证服务端永远能够及时快速响应请求,为了给用户友好地反馈信息,需要设置请求超时的处理和请求异常时的处理。
const xhr = new XMLHttpRequest()// 超时设置 2sxhr.timeout = 2000// 超时回调xhr.ontimeout = function () {alert("网络异常,请稍后重试!")}// 网络异常回调xhr.onerror = function () {alert("你的网络似乎出了一些问题!")}xhr.open("GET", "http://localhost:8000/delay")xhr.send()
请求的状态为 canceled 时,表示请求已经取消了,状态码为 0。

取消请求
在请求的过程中,当结果还没有响应时,我们可以使用 abort 方法手动取消这个请求。
let flag = falseconst xhr = new XMLHttpRequest()xhr.open("GET", "http://localhost:8000/delay")xhr.send()if (!flag) {// 取消请求xhr.abort()}
请求重复问题
如果用户频繁去发送相同请求,服务器的压力会很大。我们可以在用户发送请求的时候,判断是否发送过相同的请求,如果有,则可以取消请求并重新发送一个新的请求。
// 用来存放 XMLHttpRequest 实例let xhr = null// 标识变量,是否发送请求let isSending = falsebtn.addEventListener("click", function () {// 如果已经发送了则取消请求if (isSending) xhr.abort()// 当实例被创建时,表示正在发送请求xhr = new XMLHttpRequest()// 此时更改标识变量isSending = truexhr.open("GET", "http://localhost:8000/delay")xhr.send()xhr.onreadystatechange = function () {if (xhr.readyState === 4) {// 当请求操作完成之后,还原标识变量isSending = false}}})
