前言
之前我们学习过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));