前言

之前我们学习过HTTP协议,它是浏览器和服务器之间通信的协议。

Ajax出现之前都是用标签的资源来发起HTTP请求,比如img/srca/hrefscript/src
Ajax之前想要请求一段数据,需要进行混编模式开发,但是这样每次点击按钮请求数据的数据都会造成页面的刷新(a标签 > 点击 > 当前页面url带参数 > 页面刷新后判断URL是否存在参数,如果有参数加载数据)

混编模式:前端和后端的代码写在一起,文件拓展名是后端语言的拓展名(index.php),因为浏览器不能解析后端语言代码,但是php文件可以嵌入HTML代码

以上就是原始开发网站的模式!!!

那么如何做到不重新加载整个页面,却能获取到新的网页所需的数据和更新部分网页内容呢?

认识 Ajax

什么是Ajax
异步的JavaScriptXML(Asynchronous JavaScript and XML)
利用JavaScript脚本发起HTTP通信。
请求服务器返回JSON/XML文档,前端从XML文档中提取数据,再在不刷新整个网页的基础上,渲染到网页相应的位置。

Ajax在 1999 年之前都是通过HTML的资源发起HTTP请求,而IE5.0允许了允许JS脚本发起HTTP请求(异步)。到了 2005 谷歌地图使用异步技术更新地图服务这才得到了诸多大厂的青睐,到了 2006 年W3C发布了AJAX国际标准。

使用 Ajax

使用Ajax先要创建XMLHttpRequest实例对象和ActiveXObject实例对象(IE5IE6专用)
XMLHttpRequest是浏览器内置的构造函数,需要进行实例化,例如 new Object()new Date()new Regexp()

XMLHttpRequest的名字为什么是XML呢?
因为当时异步请求只支持XML,现在我们通常请求的是多种资源,故这个名字已经不准确了,这个名字只是延用。
AJAX 请求 XML 的案例.png

创建实例

🧽 创建Ajax实例对象:

  1. // 兼容写法
  2. var xhr;
  3. if (window.XMLHttpRequest) {
  4. xhr = new XMLHttpRequest();
  5. } else {
  6. xhr = new ActiveXObject("Microsoft.XMLHTTP");
  7. }

open 方法

:::info 使用xhr对象首先要调用open()方法,这个方法接收 3 个参数:请求类型、请求URL,以及表示请求是否异步的布尔值(true异步/false同步)。
调用open()不会实际发送请求,只是为发送请求做好准备。 :::

  1. // 兼容写法
  2. var xhr;
  3. if (window.XMLHttpRequest) {
  4. xhr = new XMLHttpRequest();
  5. } else {
  6. xhr = new ActiveXObject("Microsoft.XMLHTTP");
  7. }
  8. xhr.open("GET","/user/info",true)

send() 方法

:::info 要发送定义好的请求,必须调用send()方法, send()方法接收一个参数,是作为请求体发送的数据。如果不需要发送请求体,则必须传null,因为这个参数在某些浏览器中是必需的。 :::

  1. // 兼容写法
  2. var xhr;
  3. if (window.XMLHttpRequest) {
  4. xhr = new XMLHttpRequest();
  5. } else {
  6. xhr = new ActiveXObject("Microsoft.XMLHTTP");
  7. }
  8. xhr.open("GET","/user/info",true);
  9. xhr.send(null);

响应

:::info 收到响应后,xhr对象的以下属性会被填充上数据。

responseText:作为响应体返回的文本。
responseXML:如果响应的内容类型是”text/xml“或”application/xml“,那就是包含响应数据的XML DOM文档。
status:响应的HTTP状态。
statusText:响应的HTTP状态描述。 ::: 收到响应后,第一步要检查status属性以确保响应成功返回。一般来说,HTTP 状态码为 2xx 表示成功。此时,responseTextresponseXML(如果内容类型正确)属性中会有内容。
如果HTTP状态码是 304,则表示资源未修改过,是从浏览器缓存中直接拿取的。当然这也意味着响应有效。为确保收到正确的响应,应该检查这些状态。

readyState 和 readystatechange 事件

:::info xhr对象有一个readyState属性,表示当前处在请求/响应过程的哪个阶段。
0: 请求未初始化
1: 服务器连接已建立
2: 请求已接收
3: 请求处理中
4: 请求已完成,且响应已就绪
⚠️ 注意:
readyState仅仅是针对请求的状态码,获取资源是否成功取决于status的状态

每次readyState从一个值变成另一个值,都会触发readystatechange事件。
为保证跨浏览器兼容,onreadystatechange事件处理程序应该在调用open()之前赋值。 :::

HTTP 头部

:::info 默认情况下,xhr请求会发送相关的头信息,如果需要发送额外的请求头部,可以使用setRequestHeader()方法。
这个方法接收两个参数:头部字段的名称和值。为保证请求头部被发送,必须在open()之后、send()之前调用setRequestHeader() :::

案例

  1. var xhr;
  2. if (window.XMLHttpRequest) {
  3. xhr = new XMLHttpRequest();
  4. } else {
  5. xhr = new ActiveXObject("Microsoft.XMLHTTP");
  6. }
  7. xhr.onreadystatechange = function () {
  8. if (xhr.readyState === 4) {
  9. if ((xhr.status >= 200 && xhr.status < 300) || xhr.status < 304) {
  10. var resData = JSON.parse(xhr.responseText);
  11. console.log("success", resData);
  12. }
  13. }
  14. };
  15. xhr.open("GET", "/user/info?id=123456", true);
  16. xhr.send();
  1. var xhr;
  2. if (window.XMLHttpRequest) {
  3. xhr = new XMLHttpRequest();
  4. } else {
  5. xhr = new ActiveXObject("Microsoft.XMLHTTP");
  6. }
  7. xhr.onreadystatechange = function () {
  8. if (xhr.readyState === 4) {
  9. if ((xhr.status >= 200 && xhr.status < 300) || xhr.status < 304) {
  10. var resData = JSON.parse(xhr.responseText);
  11. console.log("success", resData);
  12. }
  13. }
  14. };
  15. xhr.open("POST", "/user/info", true);
  16. // POST 是以表单方式提交,所以需要设置请求头,setRequestHeader 必须在 send 之前设置
  17. xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
  18. // POST 参数必须进行序列化,目的是请求体中的数据转换为键值对,这是请求报文真正的样子,浏览器只是进行了美化
  19. // 后端接收到 a=1&b=2&c=3 这样的数据才知道是这是一个 POST 方式传来的数据
  20. xhr.send("a=1&b=2&c=3");

封装 Ajax

📩 接下来我们封装一下Ajax(简单版):

  1. // 立即执行函数,返回一个对象
  2. var $ = (function () {
  3. // 兼容写法
  4. var xhr = window.XMLHttpRequest
  5. ? new XMLHttpRequest()
  6. : new ActiveXObject("Microsoft.XMLHTTP");
  7. if (!xhr) {
  8. throw new Error("浏览器不支持异步发起 HTTP 请求!");
  9. }
  10. // 对象序列化
  11. function formatData(object) {
  12. var str = "";
  13. for (const key in object) {
  14. str += key + "=" + object[key] + "&";
  15. }
  16. return str.replace(/&$/, "");
  17. }
  18. // 处理 Ajax 请求
  19. function _doAjax(opt) {
  20. var opt = opt || {},
  21. type = (opt.type || "GET").toUpperCase(),
  22. url = opt.url,
  23. async = opt.async || true,
  24. data = opt.data || null,
  25. success = opt.success || function () {},
  26. error = opt.error || function () {},
  27. complete = opt.complete || function () {};
  28. if (!url) {
  29. throw new Error("url 不能为空!");
  30. }
  31. xhr.open(type, url, async);
  32. if (type === "POST") {
  33. xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
  34. }
  35. if (type === "GET") {
  36. xhr.send();
  37. } else {
  38. var str = formatData(data);
  39. xhr.send(str);
  40. }
  41. xhr.onreadystatechange = function () {
  42. if (xhr.readyState === 4 && xhr.status === 200) {
  43. success(JSON.parse(xhr.responseText));
  44. complete();
  45. } else {
  46. error();
  47. complete();
  48. }
  49. };
  50. }
  51. return {
  52. ajax: function (opt) {
  53. _doAjax(opt);
  54. },
  55. get: function (url, successCallback, errorCallback) {
  56. _doAjax({
  57. url,
  58. type: "GET",
  59. success: successCallback,
  60. error: errorCallback,
  61. });
  62. },
  63. post: function (url, data, successCallback, errorCallback) {
  64. _doAjax({
  65. url,
  66. type: "POST",
  67. data,
  68. success: successCallback,
  69. error: errorCallback,
  70. });
  71. },
  72. };
  73. })();
  74. // 调用 ajax
  75. $.ajax({
  76. url: "user/list",
  77. type: "POST",
  78. data: {
  79. a: 1,
  80. b: 2,
  81. },
  82. success: function (res) {
  83. console.log("success");
  84. },
  85. error: function (res) {
  86. console.log("error");
  87. },
  88. });
  89. // 调用 GET 方法
  90. $.get(
  91. "user/detail",
  92. function (res) {
  93. console.log("success");
  94. },
  95. function (res) {
  96. console.log("error");
  97. }
  98. );
  99. // 调用 POST 方法
  100. $.post(
  101. "/user/edit",
  102. {
  103. a: 1,
  104. b: 2,
  105. },
  106. function (res) {
  107. console.log("success");
  108. },
  109. function (res) {
  110. console.log("error");
  111. }
  112. );

XMLHttpRequest Level2

XMLHttpRequest标准又分为Level 1Level 2

📒XMLHttpRequest Level 1缺点:
1、无法发送跨域请求
2、不能非纯文本的数据
3、无法获取传输进度

📕XMLHttpRequest Level 2改进:
1、可以发送跨域请求
2、支持获取二进制数据(非纯文本数据)
3、支持上传文件
4、formData对象
5、可以获取传输进度
6、可以设置超时时间

📗 兼容性问题
1、IE8/9/Opara Mini不支持xhr对象
2、IE10/11不支持响应类型为JSON
3、部分浏览器不支持超时设置
4、部分浏览器不支持blob(文件对象的二进制数据)

事件

:::info xhr.onloadstart: 绑定HTTP 请求发出的监听函数
xhr.onload: 绑定请求成功完成的监听函数
xhr.onerror:绑定请求失败的监听函数
xhr.onabort: 绑定请求中止(调用了**abort()**方法)的监听函数
xhr.onloadend: 绑定请求完成(不管成功与失败)的监听函数 :::

  1. var xhr;
  2. if (window.XMLHttpRequest) {
  3. xhr = new XMLHttpRequest();
  4. } else {
  5. xhr = new ActiveXObject("Microsoft.XMLHTTP");
  6. }
  7. xhr.onloadstart = function () {
  8. console.log("onloadstart");
  9. };
  10. xhr.onload = function () {
  11. console.log("onload");
  12. };
  13. xhr.onerror = function () {
  14. console.log("onerror");
  15. };
  16. xhr.onabort = function () {
  17. console.log("onabort");
  18. };
  19. xhr.onloadend = function () {
  20. console.log("onloadend");
  21. };
  22. xhr.onreadystatechange = function () {
  23. if (xhr.readyState === 4) {
  24. if ((xhr.status >= 200 && xhr.status < 300) || xhr.status < 304) {
  25. console.log("success", xhr.responseText);
  26. }
  27. }
  28. };
  29. xhr.open("POST", "/user/info", true);
  30. xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
  31. xhr.send("a=1&b=2");

超时

:::info xhr对象增加了一个timeout属性,用于表示发送请求后等待多少毫秒,如果响应不成功就中断请求,当请求超时后会触发ontimeout事件。 :::

  1. var xhr;
  2. if (window.XMLHttpRequest) {
  3. xhr = new XMLHttpRequest();
  4. } else {
  5. xhr = new ActiveXObject("Microsoft.XMLHTTP");
  6. }
  7. xhr.ontimeout = function() {
  8. alert("Request did not return in a second.");
  9. };
  10. xhr.onreadystatechange = function () {
  11. if (xhr.readyState === 4) {
  12. if ((xhr.status >= 200 && xhr.status < 300) || xhr.status < 304) {
  13. console.log("success", xhr.responseText);
  14. }
  15. }
  16. };
  17. xhr.open("POST", "/user/info", true);
  18. xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
  19. xhr.timeout = 1000; // 设置 1 秒超时
  20. xhr.send("a=1&b=2");

FormData 类型

:::info FormData类型便于表单序列化,也便于创建与表单类似格式的数据然后通过xhr发送。
append()方法接收两个参数:键和值,相当于表单字段名称和该字段的值。 :::

  1. let data = new FormData();
  2. data.append("name", "Nicholas");

有了FormData实例,可以直接传给xhr对象的send()方法,使用FormData不再需要给xhr对象显式设置任何请求头部了。xhr对象能够识别作为FormData实例传入的数据类型并自动配置相应的头部。

  1. var 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("post", "postexample.php", true);
  12. let form = document.getElementById("user-info");
  13. xhr.send(new FormData(form));

更多