前言
之前我们学习过HTTP协议,它是浏览器和服务器之间通信的协议。
在Ajax出现之前都是用标签的资源来发起HTTP请求,比如img/src、a/href、script/src。
在Ajax之前想要请求一段数据,需要进行混编模式开发,但是这样每次点击按钮请求数据的数据都会造成页面的刷新(a标签 > 点击 > 当前页面url带参数 > 页面刷新后判断URL是否存在参数,如果有参数加载数据)
混编模式:前端和后端的代码写在一起,文件拓展名是后端语言的拓展名(
index.php),因为浏览器不能解析后端语言代码,但是php文件可以嵌入HTML代码
以上就是原始开发网站的模式!!!
那么如何做到不重新加载整个页面,却能获取到新的网页所需的数据和更新部分网页内容呢?
认识 Ajax
什么是Ajax?
异步的JavaScript和XML(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实例对象(IE5和IE6专用)XMLHttpRequest是浏览器内置的构造函数,需要进行实例化,例如 new Object()、new Date()、new Regexp()
XMLHttpRequest的名字为什么是XML呢?
因为当时异步请求只支持XML,现在我们通常请求的是多种资源,故这个名字已经不准确了,这个名字只是延用。
创建实例
🧽 创建Ajax实例对象:
// 兼容写法var xhr;if (window.XMLHttpRequest) {xhr = new XMLHttpRequest();} else {xhr = new ActiveXObject("Microsoft.XMLHTTP");}
open 方法
:::info
使用xhr对象首先要调用open()方法,这个方法接收 3 个参数:请求类型、请求URL,以及表示请求是否异步的布尔值(true异步/false同步)。
调用open()不会实际发送请求,只是为发送请求做好准备。
:::
// 兼容写法var xhr;if (window.XMLHttpRequest) {xhr = new XMLHttpRequest();} else {xhr = new ActiveXObject("Microsoft.XMLHTTP");}xhr.open("GET","/user/info",true)
send() 方法
:::info
要发送定义好的请求,必须调用send()方法, send()方法接收一个参数,是作为请求体发送的数据。如果不需要发送请求体,则必须传null,因为这个参数在某些浏览器中是必需的。
:::
// 兼容写法var xhr;if (window.XMLHttpRequest) {xhr = new XMLHttpRequest();} else {xhr = new ActiveXObject("Microsoft.XMLHTTP");}xhr.open("GET","/user/info",true);xhr.send(null);
响应
:::info
收到响应后,xhr对象的以下属性会被填充上数据。
responseText:作为响应体返回的文本。 responseXML:如果响应的内容类型是”text/xml“或”application/xml“,那就是包含响应数据的XML DOM文档。 status:响应的HTTP状态。 statusText:响应的HTTP状态描述。
:::
收到响应后,第一步要检查status属性以确保响应成功返回。一般来说,HTTP 状态码为 2xx 表示成功。此时,responseText或 responseXML(如果内容类型正确)属性中会有内容。
如果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()
:::
案例
var xhr;if (window.XMLHttpRequest) {xhr = new XMLHttpRequest();} else {xhr = new ActiveXObject("Microsoft.XMLHTTP");}xhr.onreadystatechange = function () {if (xhr.readyState === 4) {if ((xhr.status >= 200 && xhr.status < 300) || xhr.status < 304) {var resData = JSON.parse(xhr.responseText);console.log("success", resData);}}};xhr.open("GET", "/user/info?id=123456", true);xhr.send();
var xhr;if (window.XMLHttpRequest) {xhr = new XMLHttpRequest();} else {xhr = new ActiveXObject("Microsoft.XMLHTTP");}xhr.onreadystatechange = function () {if (xhr.readyState === 4) {if ((xhr.status >= 200 && xhr.status < 300) || xhr.status < 304) {var resData = JSON.parse(xhr.responseText);console.log("success", resData);}}};xhr.open("POST", "/user/info", true);// POST 是以表单方式提交,所以需要设置请求头,setRequestHeader 必须在 send 之前设置xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");// POST 参数必须进行序列化,目的是请求体中的数据转换为键值对,这是请求报文真正的样子,浏览器只是进行了美化// 后端接收到 a=1&b=2&c=3 这样的数据才知道是这是一个 POST 方式传来的数据xhr.send("a=1&b=2&c=3");
封装 Ajax
📩 接下来我们封装一下Ajax(简单版):
// 立即执行函数,返回一个对象var $ = (function () {// 兼容写法var xhr = window.XMLHttpRequest? new XMLHttpRequest(): new ActiveXObject("Microsoft.XMLHTTP");if (!xhr) {throw new Error("浏览器不支持异步发起 HTTP 请求!");}// 对象序列化function formatData(object) {var str = "";for (const key in object) {str += key + "=" + object[key] + "&";}return str.replace(/&$/, "");}// 处理 Ajax 请求function _doAjax(opt) {var opt = opt || {},type = (opt.type || "GET").toUpperCase(),url = opt.url,async = opt.async || true,data = opt.data || null,success = opt.success || function () {},error = opt.error || function () {},complete = opt.complete || function () {};if (!url) {throw new Error("url 不能为空!");}xhr.open(type, url, async);if (type === "POST") {xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");}if (type === "GET") {xhr.send();} else {var str = formatData(data);xhr.send(str);}xhr.onreadystatechange = function () {if (xhr.readyState === 4 && xhr.status === 200) {success(JSON.parse(xhr.responseText));complete();} else {error();complete();}};}return {ajax: function (opt) {_doAjax(opt);},get: function (url, successCallback, errorCallback) {_doAjax({url,type: "GET",success: successCallback,error: errorCallback,});},post: function (url, data, successCallback, errorCallback) {_doAjax({url,type: "POST",data,success: successCallback,error: errorCallback,});},};})();// 调用 ajax$.ajax({url: "user/list",type: "POST",data: {a: 1,b: 2,},success: function (res) {console.log("success");},error: function (res) {console.log("error");},});// 调用 GET 方法$.get("user/detail",function (res) {console.log("success");},function (res) {console.log("error");});// 调用 POST 方法$.post("/user/edit",{a: 1,b: 2,},function (res) {console.log("success");},function (res) {console.log("error");});
XMLHttpRequest Level2
XMLHttpRequest标准又分为Level 1和Level 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: 绑定请求完成(不管成功与失败)的监听函数
:::
var xhr;if (window.XMLHttpRequest) {xhr = new XMLHttpRequest();} else {xhr = new ActiveXObject("Microsoft.XMLHTTP");}xhr.onloadstart = function () {console.log("onloadstart");};xhr.onload = function () {console.log("onload");};xhr.onerror = function () {console.log("onerror");};xhr.onabort = function () {console.log("onabort");};xhr.onloadend = function () {console.log("onloadend");};xhr.onreadystatechange = function () {if (xhr.readyState === 4) {if ((xhr.status >= 200 && xhr.status < 300) || xhr.status < 304) {console.log("success", xhr.responseText);}}};xhr.open("POST", "/user/info", true);xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");xhr.send("a=1&b=2");
超时
:::info
xhr对象增加了一个timeout属性,用于表示发送请求后等待多少毫秒,如果响应不成功就中断请求,当请求超时后会触发ontimeout事件。
:::
var xhr;if (window.XMLHttpRequest) {xhr = new XMLHttpRequest();} else {xhr = new ActiveXObject("Microsoft.XMLHTTP");}xhr.ontimeout = function() {alert("Request did not return in a second.");};xhr.onreadystatechange = function () {if (xhr.readyState === 4) {if ((xhr.status >= 200 && xhr.status < 300) || xhr.status < 304) {console.log("success", xhr.responseText);}}};xhr.open("POST", "/user/info", true);xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");xhr.timeout = 1000; // 设置 1 秒超时xhr.send("a=1&b=2");
FormData 类型
:::info
FormData类型便于表单序列化,也便于创建与表单类似格式的数据然后通过xhr发送。append()方法接收两个参数:键和值,相当于表单字段名称和该字段的值。
:::
let data = new FormData();data.append("name", "Nicholas");
有了FormData实例,可以直接传给xhr对象的send()方法,使用FormData不再需要给xhr对象显式设置任何请求头部了。xhr对象能够识别作为FormData实例传入的数据类型并自动配置相应的头部。
var 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("post", "postexample.php", true);let form = document.getElementById("user-info");xhr.send(new FormData(form));
