🥇 XHR MDN
https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest
🥇 理解 XMLHttpRequest 对象
使用 XMLHttpRequest (XHR) 对象可以与服务器交互,也就是发送 ajax 请求
前端可以获取到数据,而无需让整个的页面刷新。
这使得 Web 页面可以只更新页面的局部,而不影响用户的操作。
所有现代浏览器都通过 XMLHttpRequest 构造函数原生支持 XHR 对象:
let xhr = new XMLHttpRequest();
🥇 使用 XHR
使用 XHR 对象首先要调用 open()方法,这个方法接收 3 个参数:请求类型(”get”、”post”等)、请求 URL,以及表示请求是否异步的布尔值。下面是一个例子:
xhr.open("get", "example.php", false);
这行代码就可以向 example.php 发送一个同步的 GET 请求。关于这行代码需要说明几点。首先,这里的 URL 是相对于代码所在页面的,当然也可以使用绝对 URL。其次,调用 open()不会实际发送请求,只是为发送请求做好准备。
只能访问同源 URL,也就是域名相同、端口相同、协议相同。如果请求的 URL 与发送请求的页面在任何方面有所不同,则会抛出安全错误。
要发送定义好的请求,必须像下面这样调用 send()方法:
xhr.open("get", "example.txt", false);
xhr.send(null);
send()方法接收一个参数,是作为请求体发送的数据。如果不需要发送请求体,则必须传 null,因为这个参数在某些浏览器中是必需的。调用 send()之后,请求就会发送到服务器。
因为这个请求是同步的,所以 JavaScript 代码会等待服务器响应之后再继续执行。收到响应后,XHR对象的以下属性会被填充上数据。
1. responseText:作为响应体返回的文本。
2. responseXML:如果响应的内容类型是"text/xml"或"application/xml",那就是包含响应数据的 XML DOM 文档。
3. status:响应的 HTTP 状态。
4. statusText:响应的 HTTP 状态描述。
收到响应后,第一步要检查 status 属性以确保响应成功返回。一般来说,HTTP 状态码为 2xx 表示成功。此时,responseText 或 responseXML(如果内容类型正确)属性中会有内容。如果 HTTP状态码是 304,则表示资源未修改过,是从浏览器缓存中直接拿取的。当然这也意味着响应有效。为确保收到正确的响应,应该检查这些状态,如下所示:
xhr.open("get", "example.txt", false);
xhr.send(null);
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {
alert(xhr.responseText);
} else {
alert("Request was unsuccessful: " + xhr.status);
}
以上代码可能显示服务器返回的内容,也可能显示错误消息,取决于 HTTP 响应的状态码。为确定下一步该执行什么操作,最好检查 status 而不是 statusText 属性,因为后者已经被证明在跨浏览器的情况下不可靠。无论是什么响应内容类型,responseText 属性始终会保存响应体,而 responseXML 则对于非 XML 数据是 null。
虽然可以像前面的例子一样发送同步请求,但多数情况下最好使用异步请求,这样可以不阻塞 JavaScript 代码继续执行。XHR 对象有一个 readyState 属性,表示当前处在请求/响应过程的哪个阶段。这个属性有如下可能的值。
1. 0:未初始化(Uninitialized)。尚未调用 open() 方法。
2. 1:已打开(Open)。已调用 open() 方法,尚未调用 send() 方法。
3. 2:已发送(Sent)。已调用 send()方法,尚未收到响应。
4. 3:接收中(Receiving)。已经收到部分响应。
5. 4:完成(Complete)。已经收到所有响应,可以使用了。
每次 readyState 从一个值变成另一个值,都会触发 readystatechange 事件。可以借此机会检查 readyState 的值。一般来说,我们唯一关心的 readyState 值是 4,表示数据已就绪。为保证跨浏览器兼容,onreadystatechange 事件处理程序应该在调用 open()之前赋值。来看下面的例子:
let xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
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.txt", true);
xhr.send(null);
在收到响应之前如果想取消异步请求,可以调用 abort()方法:
xhr.abort();
调用这个方法后,XHR 对象会停止触发事件,并阻止访问这个对象上任何与响应相关的属性。中断请求后,应该取消对 XHR 对象的引用。由于内存问题,不推荐重用 XHR 对象。
🥇 HTTP 头部
每个 HTTP 请求和响应都会携带一些头部字段,这些字段可能对开发者有用。XHR 对象会通过一些方法暴露与请求和响应相关的头部字段。
默认情况下,XHR 请求会发送以下头部字段
1. Accept:浏览器可以处理的内容类型。
2. Accept-Charset:浏览器可以显示的字符集。
3. Accept-Encoding:浏览器可以处理的压缩编码类型。
4. Accept-Language:浏览器使用的语言。
5. Connection:浏览器与服务器的连接类型。
6. Cookie:页面中设置的 Cookie。 Host:发送请求的页面所在的域。
7. Referer:发送请求的页面的 URI。注意,这个字段在 HTTP 规范中就拼错了,所以考虑到兼容性也必须将错就错。(正确的拼写应该是 Referrer。)
8. User-Agent:浏览器的用户代理字符串。
虽然不同浏览器发送的确切头部字段可能各不相同,但这些通常都是会发送的。如果需要发送额外的请求头部,可以使用 setRequestHeader()方法。这个方法接收两个参数:头部字段的名称和值。为保证请求头部被发送,必须在 open()之后、send()之前调用 setRequestHeader(),如下面的例子所示:
let xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
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.setRequestHeader("MyHeader", "MyValue"); // 请求头
xhr.send(null);
服务器通过读取自定义头部可以确定适当的操作。自定义头部一定要区别于浏览器正常发送的头部,否则可能影响服务器正常响应。有些浏览器允许重写默认头部,有些浏览器则不允许。
可以使用 getResponseHeader()方法从 XHR 对象获取响应头部,只要传入要获取头部的名称即可。如果想取得所有响应头部,可以使用getAllResponseHeaders() 方法,这个方法会返回包含所有响应头部的字符串。下面是调用这两个方法的例子:
let myHeader = xhr.getResponseHeader("MyHeader");
let allHeaders xhr.getAllResponseHeaders();
服务器可以使用头部向浏览器传递额外的结构化数据。getAllResponseHeaders() 方法通常返回类似如下的字符串:
Date: Sun, 14 Nov 2004 18:04:03 GMT
Server: Apache/1.3.29 (Unix)
Vary: Accept
X-Powered-By: PHP/4.3.8
Connection: close
Content-Type: text/html; charset=iso-8859-1
通过解析以上头部字段的输出,就可以知道服务器发送的所有头部,而不需要单独去检查了。
🥇 区别 ajax 请求与一般 HTTP 请求
ajax 请求是一种特别的 http 请求: 只有通过 XHR/fetch 发送的是 ajax 请求, 其它都是一般 HTTP 请求
对服务器端来说, 没有任何区别, 区别在浏览器端
浏览器端发请求: 只有 XHR 或 fetch 发出的才是 ajax 请求, 其它所有的都是非 ajax 请求
浏览器端接收到响应
一般请求: 浏览器一般会直接显示响应体数据, 也就是我们常说的刷新/跳转页面
ajax请求: 浏览器不会对界面进行任何更新操作, 只是调用监视的回调函数并传入响应相关数据
🥇 使用XHR封装一个发ajax请求的通用函数
函数的返回值为promise, 成功的结果为response, 异常的结果为error
能处理多种类型的请求: GET/POST/PUT/DELETE
函数的参数为一个配置对象: url/method/params/data
响应json数据自动解析为了js
1. 返回一个promise对象
2. 函数参数是一个配置对象
3. 创建建xhr对象发ajax请求
4. 携带请求参数
5. 读取响应数据, 更新promise
XHR 简易版 ajax
XHR的ajax封装(简单版axios)
1. 函数的返回值为 promise, 成功的结果为 response, 异常的结果为 error
2. 能处理多种类型的请求: GET / POST / PUT / DELETE
3. 函数的参数为一个配置对象 {
url: '', // 请求地址
method: '', // 请求方式 GET / POST / PUT / DELETE
params: {}, // GET / DELETE 请求的query参数
data: {}, // POST 或 PUT 请求的请求体参数
}
4. 响应 json 数据自动解析为 js
// 简单的封装
function axios({
url,
method = 'GET',
params = {},
data = {}
}) {
// 准备 query 参数 例: {a:1,b:2} > a=1&b=2 > url?a=1&b=2
let queryStr = '';
Object.keys(params).forEach(key => {
queryStr += `&${key}=${params[key]}`
});
if (queryStr) {
queryStr = queryStr.substring(1);
url += '?' + queryStr;
}
// 返回一个 Promise
return new Promise((resolve, reject) => {
// 创建 XHR 对象
const xhr = new XMLHttpRequest();
// 绑定监听
xhr.onreadystatechange = () => {
const {
readyState,
status,
statusText
} = xhr;
// 如果请求未结束,直接返回
if (readyState !== 4) return;
// 请求成功,调用 resolve
if (status >= 200 && status < 300) {
const response = {
data: JSON.parse(xhr.response),
status,
statusText
}
resolve();
}
// 请求失败,调用 reject
else {
reject('请求失败');
}
};
// 初始化
xhr.open(method, url, true);
// 发送请求
if (method === "POST" || method === "PUT") {
xhr.setRequestHeader('Content-Type', 'application/json;charset=utf-8')
xhr.send(JSON.stringify(data));
} else {
xhr.send();
}
})
}
🥇 API
- XMLHttpRequest(): 创建XHR对象的构造函数
- status: 响应状态码值, 比如200, 404
- statusText: 响应状态文本
- readyState: 标识请求状态的只读属性
- 初始
- open()之后
- send()之后
- 请求中
- 请求完成
- onreadystatechange: 绑定readyState改变的监听
- responseType: 指定响应数据类型, 如果是’json’, 得到响应后自动解析响应体数据
- response: 响应体数据, 类型取决于responseType的指定
- timeout: 指定请求超时时间, 默认为0代表没有限制
- ontimeout: 绑定超时的监听
- onerror: 绑定请求网络错误的监听
- open(): 初始化一个请求, 参数为: (method, url[, async])
- send(data): 发送请求
- abort(): 中断请求
- getResponseHeader(name): 获取指定名称的响应头值
- getAllResponseHeaders(): 获取所有响应头组成的字符串
- setRequestHeader(name, value): 设置请求头