🥇 XHR MDN

https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest

🥇 理解 XMLHttpRequest 对象

使用 XMLHttpRequest (XHR) 对象可以与服务器交互,也就是发送 ajax 请求
前端可以获取到数据,而无需让整个的页面刷新。
这使得 Web 页面可以只更新页面的局部,而不影响用户的操作。
所有现代浏览器都通过 XMLHttpRequest 构造函数原生支持 XHR 对象:

  1. let xhr = new XMLHttpRequest();

🥇 使用 XHR

使用 XHR 对象首先要调用 open()方法,这个方法接收 3 个参数:请求类型(”get”、”post”等)、请求 URL,以及表示请求是否异步的布尔值。下面是一个例子:

  1. xhr.open("get", "example.php", false);

这行代码就可以向 example.php 发送一个同步的 GET 请求。关于这行代码需要说明几点。首先,这里的 URL 是相对于代码所在页面的,当然也可以使用绝对 URL。其次,调用 open()不会实际发送请求,只是为发送请求做好准备。

只能访问同源 URL,也就是域名相同、端口相同、协议相同。如果请求的 URL 与发送请求的页面在任何方面有所不同,则会抛出安全错误。

要发送定义好的请求,必须像下面这样调用 send()方法:

  1. xhr.open("get", "example.txt", false);
  2. xhr.send(null);

send()方法接收一个参数,是作为请求体发送的数据。如果不需要发送请求体,则必须传 null,因为这个参数在某些浏览器中是必需的。调用 send()之后,请求就会发送到服务器。
因为这个请求是同步的,所以 JavaScript 代码会等待服务器响应之后再继续执行。收到响应后,XHR对象的以下属性会被填充上数据。

  1. 1. responseText:作为响应体返回的文本。
  2. 2. responseXML:如果响应的内容类型是"text/xml""application/xml",那就是包含响应数据的 XML DOM 文档。
  3. 3. status:响应的 HTTP 状态。
  4. 4. statusText:响应的 HTTP 状态描述。

收到响应后,第一步要检查 status 属性以确保响应成功返回。一般来说,HTTP 状态码为 2xx 表示成功。此时,responseText 或 responseXML(如果内容类型正确)属性中会有内容。如果 HTTP状态码是 304,则表示资源未修改过,是从浏览器缓存中直接拿取的。当然这也意味着响应有效。为确保收到正确的响应,应该检查这些状态,如下所示:

  1. xhr.open("get", "example.txt", false);
  2. xhr.send(null);
  3. if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {
  4. alert(xhr.responseText);
  5. } else {
  6. alert("Request was unsuccessful: " + xhr.status);
  7. }

以上代码可能显示服务器返回的内容,也可能显示错误消息,取决于 HTTP 响应的状态码。为确定下一步该执行什么操作,最好检查 status 而不是 statusText 属性,因为后者已经被证明在跨浏览器的情况下不可靠。无论是什么响应内容类型,responseText 属性始终会保存响应体,而 responseXML 则对于非 XML 数据是 null。
虽然可以像前面的例子一样发送同步请求,但多数情况下最好使用异步请求,这样可以不阻塞 JavaScript 代码继续执行。XHR 对象有一个 readyState 属性,表示当前处在请求/响应过程的哪个阶段。这个属性有如下可能的值。

  1. 1. 0:未初始化(Uninitialized)。尚未调用 open() 方法。
  2. 2. 1:已打开(Open)。已调用 open() 方法,尚未调用 send() 方法。
  3. 3. 2:已发送(Sent)。已调用 send()方法,尚未收到响应。
  4. 4. 3:接收中(Receiving)。已经收到部分响应。
  5. 5. 4:完成(Complete)。已经收到所有响应,可以使用了。

每次 readyState 从一个值变成另一个值,都会触发 readystatechange 事件。可以借此机会检查 readyState 的值。一般来说,我们唯一关心的 readyState 值是 4,表示数据已就绪。为保证跨浏览器兼容,onreadystatechange 事件处理程序应该在调用 open()之前赋值。来看下面的例子:

  1. let xhr = new XMLHttpRequest();
  2. xhr.onreadystatechange = function () {
  3. if (xhr.readyState == 4) {
  4. if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {
  5. alert(xhr.responseText);
  6. } else {
  7. alert("Request was unsuccessful: " + xhr.status);
  8. }
  9. }
  10. };
  11. xhr.open("get", "example.txt", true);
  12. xhr.send(null);

在收到响应之前如果想取消异步请求,可以调用 abort()方法:

  1. xhr.abort();

调用这个方法后,XHR 对象会停止触发事件,并阻止访问这个对象上任何与响应相关的属性。中断请求后,应该取消对 XHR 对象的引用。由于内存问题,不推荐重用 XHR 对象。

🥇 HTTP 头部

每个 HTTP 请求和响应都会携带一些头部字段,这些字段可能对开发者有用。XHR 对象会通过一些方法暴露与请求和响应相关的头部字段。
默认情况下,XHR 请求会发送以下头部字段

  1. 1. Accept:浏览器可以处理的内容类型。
  2. 2. Accept-Charset:浏览器可以显示的字符集。
  3. 3. Accept-Encoding:浏览器可以处理的压缩编码类型。
  4. 4. Accept-Language:浏览器使用的语言。
  5. 5. Connection:浏览器与服务器的连接类型。
  6. 6. Cookie:页面中设置的 Cookie Host:发送请求的页面所在的域。
  7. 7. Referer:发送请求的页面的 URI。注意,这个字段在 HTTP 规范中就拼错了,所以考虑到兼容性也必须将错就错。(正确的拼写应该是 Referrer。)
  8. 8. User-Agent:浏览器的用户代理字符串。

虽然不同浏览器发送的确切头部字段可能各不相同,但这些通常都是会发送的。如果需要发送额外的请求头部,可以使用 setRequestHeader()方法。这个方法接收两个参数:头部字段的名称和值。为保证请求头部被发送,必须在 open()之后、send()之前调用 setRequestHeader(),如下面的例子所示:

  1. let xhr = new XMLHttpRequest();
  2. xhr.onreadystatechange = function () {
  3. if (xhr.readyState == 4) {
  4. if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {
  5. alert(xhr.responseText);
  6. } else {
  7. alert("Request was unsuccessful: " + xhr.status);
  8. }
  9. }
  10. };
  11. xhr.open("get", "example.php", true);
  12. xhr.setRequestHeader("MyHeader", "MyValue"); // 请求头
  13. xhr.send(null);

服务器通过读取自定义头部可以确定适当的操作。自定义头部一定要区别于浏览器正常发送的头部,否则可能影响服务器正常响应。有些浏览器允许重写默认头部,有些浏览器则不允许。
可以使用 getResponseHeader()方法从 XHR 对象获取响应头部,只要传入要获取头部的名称即可。如果想取得所有响应头部,可以使用getAllResponseHeaders() 方法,这个方法会返回包含所有响应头部的字符串。下面是调用这两个方法的例子:

  1. let myHeader = xhr.getResponseHeader("MyHeader");
  2. let allHeaders xhr.getAllResponseHeaders();

服务器可以使用头部向浏览器传递额外的结构化数据。getAllResponseHeaders() 方法通常返回类似如下的字符串:

  1. Date: Sun, 14 Nov 2004 18:04:03 GMT
  2. Server: Apache/1.3.29 (Unix)
  3. Vary: Accept
  4. X-Powered-By: PHP/4.3.8
  5. Connection: close
  6. Content-Type: text/html; charset=iso-8859-1

通过解析以上头部字段的输出,就可以知道服务器发送的所有头部,而不需要单独去检查了。

🥇 区别 ajax 请求与一般 HTTP 请求

  1. ajax 请求是一种特别的 http 请求: 只有通过 XHR/fetch 发送的是 ajax 请求, 其它都是一般 HTTP 请求
  2. 对服务器端来说, 没有任何区别, 区别在浏览器端
  3. 浏览器端发请求: 只有 XHR fetch 发出的才是 ajax 请求, 其它所有的都是非 ajax 请求
  4. 浏览器端接收到响应
  5. 一般请求: 浏览器一般会直接显示响应体数据, 也就是我们常说的刷新/跳转页面
  6. ajax请求: 浏览器不会对界面进行任何更新操作, 只是调用监视的回调函数并传入响应相关数据

🥇 使用XHR封装一个发ajax请求的通用函数

  1. 函数的返回值为promise, 成功的结果为response, 异常的结果为error
  2. 能处理多种类型的请求: GET/POST/PUT/DELETE
  3. 函数的参数为一个配置对象: url/method/params/data
  4. 响应json数据自动解析为了js
  5. 1. 返回一个promise对象
  6. 2. 函数参数是一个配置对象
  7. 3. 创建建xhr对象发ajax请求
  8. 4. 携带请求参数
  9. 5. 读取响应数据, 更新promise
  10. XHR 简易版 ajax
  11. XHRajax封装(简单版axios)
  12. 1. 函数的返回值为 promise, 成功的结果为 response, 异常的结果为 error
  13. 2. 能处理多种类型的请求: GET / POST / PUT / DELETE
  14. 3. 函数的参数为一个配置对象 {
  15. url: '', // 请求地址
  16. method: '', // 请求方式 GET / POST / PUT / DELETE
  17. params: {}, // GET / DELETE 请求的query参数
  18. data: {}, // POST 或 PUT 请求的请求体参数
  19. }
  20. 4. 响应 json 数据自动解析为 js
  21. // 简单的封装
  22. function axios({
  23. url,
  24. method = 'GET',
  25. params = {},
  26. data = {}
  27. }) {
  28. // 准备 query 参数 例: {a:1,b:2} > a=1&b=2 > url?a=1&b=2
  29. let queryStr = '';
  30. Object.keys(params).forEach(key => {
  31. queryStr += `&${key}=${params[key]}`
  32. });
  33. if (queryStr) {
  34. queryStr = queryStr.substring(1);
  35. url += '?' + queryStr;
  36. }
  37. // 返回一个 Promise
  38. return new Promise((resolve, reject) => {
  39. // 创建 XHR 对象
  40. const xhr = new XMLHttpRequest();
  41. // 绑定监听
  42. xhr.onreadystatechange = () => {
  43. const {
  44. readyState,
  45. status,
  46. statusText
  47. } = xhr;
  48. // 如果请求未结束,直接返回
  49. if (readyState !== 4) return;
  50. // 请求成功,调用 resolve
  51. if (status >= 200 && status < 300) {
  52. const response = {
  53. data: JSON.parse(xhr.response),
  54. status,
  55. statusText
  56. }
  57. resolve();
  58. }
  59. // 请求失败,调用 reject
  60. else {
  61. reject('请求失败');
  62. }
  63. };
  64. // 初始化
  65. xhr.open(method, url, true);
  66. // 发送请求
  67. if (method === "POST" || method === "PUT") {
  68. xhr.setRequestHeader('Content-Type', 'application/json;charset=utf-8')
  69. xhr.send(JSON.stringify(data));
  70. } else {
  71. xhr.send();
  72. }
  73. })
  74. }

🥇 API

  1. XMLHttpRequest(): 创建XHR对象的构造函数
  2. status: 响应状态码值, 比如200, 404
  3. statusText: 响应状态文本
  4. readyState: 标识请求状态的只读属性
  5. 初始
  6. open()之后
  7. send()之后
  8. 请求中
  9. 请求完成
  10. onreadystatechange: 绑定readyState改变的监听
  11. responseType: 指定响应数据类型, 如果是’json’, 得到响应后自动解析响应体数据
  12. response: 响应体数据, 类型取决于responseType的指定
  13. timeout: 指定请求超时时间, 默认为0代表没有限制
  14. ontimeout: 绑定超时的监听
  15. onerror: 绑定请求网络错误的监听
  16. open(): 初始化一个请求, 参数为: (method, url[, async])
  17. send(data): 发送请求
  18. abort(): 中断请求
  19. getResponseHeader(name): 获取指定名称的响应头值
  20. getAllResponseHeaders(): 获取所有响应头组成的字符串
  21. setRequestHeader(name, value): 设置请求头