一个从、Ajax簡介
1.Ajax相關(guān)
AJAX 全稱為Asynchronous Javascript And XML伶椿,就是異步的 JS 和 XML月褥。
通過AJAX可以在瀏覽器中向服務(wù)器發(fā)送異步請求等浊,最大的優(yōu)勢:無刷新獲取數(shù)據(jù)欲诺。
AJAX 不是新的編程語言抄谐,不是新的一門獨(dú)立的技術(shù),而是一種使用現(xiàn)有標(biāo)準(zhǔn)的新方法扰法。
2.XML相關(guān)
XML 可擴(kuò)展標(biāo)記語言蛹含。
XML 被設(shè)計(jì)用來傳輸和存儲數(shù)據(jù)。
XML和HTML類似塞颁,不同的是HTML中都是預(yù)定義標(biāo)簽浦箱,而XML中沒有預(yù)定義標(biāo)簽,全都是自定義標(biāo)簽祠锣,用來表示一些數(shù)據(jù)酷窥。
例如一個學(xué)生數(shù)據(jù):name = "孫悟空" ; age = 18 ; gender = "男" ;
用XML表示:
<student>
<name>孫悟空</name>
<age>18</age>
<gender>男</gender>
</student>
用JSON表示:{"name":"孫悟空","age":18,"gender":"男"}
如今XML已經(jīng)被JSON取代了
3.Ajax的工作原理
Ajax的工作原理相當(dāng)于在用戶和服務(wù)器之間加了一個中間層(Ajax引擎),使用戶操作與服務(wù)器響應(yīng)異步化伴网。
4.Ajax的特點(diǎn)
- 優(yōu)點(diǎn)
可以無需刷新頁面而與服務(wù)器端進(jìn)行通信蓬推。
允許你根據(jù)用戶事件來更新部分頁面內(nèi)容。 - 缺點(diǎn)
沒有瀏覽歷史澡腾,不能回退
存在跨域問題
SEO不友好
二沸伏、原生Ajax
1.原生Ajax發(fā)送get請求
步驟:
實(shí)例化一個XMLHttpRequest對象
XMLHttpRequest是核心對象,Ajax的所有操作都是通過該對象進(jìn)行的动分。-
給對象綁定一個事件監(jiān)聽
在xhr內(nèi)部有五種狀態(tài)
- 0:當(dāng)xhr被實(shí)例化出來毅糟,狀態(tài)就是0,即初始化狀態(tài)
- 1:請求還沒有發(fā)出去澜公,即:send方法還沒有被調(diào)用姆另,依然可以修改請求頭
- 2:請求已經(jīng)發(fā)出去了,即:send方法已經(jīng)被調(diào)用了玛瘸,不能再修改請求頭蜕青,響應(yīng)首行和響應(yīng)頭已經(jīng)回來了
- 3:數(shù)據(jù)回來了,但是數(shù)據(jù)可能不完整糊渊,如果數(shù)據(jù)小右核,會在此階段直接接受完畢,數(shù)據(jù)大則有待于進(jìn)一步接受
- 4:數(shù)據(jù)完全回來了
指定發(fā)送請求的方式渺绒,地址贺喝,參數(shù)
發(fā)送請求
btn.onclick=function(){
//1.實(shí)例化一個XMLHttpRequest對象
const xhr=new XMLHttpRequest();
//2.給對象綁定一個事件監(jiān)聽
xhr.onreadystatechange=function(){
if(xhr.readyState===4&&xhr.status===200){
console.log(xhr.response);
let demo=document.getElementById("demo");
demo.innerHTML=xhr.response;
}
};
//3.指定發(fā)送請求的方式菱鸥,地址,參數(shù)
xhr.open("GET","http://localhost:3000/test_get?name=dexter&&age=20");
//4.發(fā)送請求
xhr.send();
};
對于readyState
狀態(tài)碼的說明
一般不會在0,1,2,3這幾種狀態(tài)的回調(diào)中躏鱼,做任何邏輯
const xhr=new XMLHttpRequest();
if(xhr.readyState===0){
console.log("我出生了");
}
xhr.onreadystatechange = function () {
if (xhr.readyState === 1) {
xhr.setRequestHeader("demo", 123);
console.log("請求還沒有發(fā)出去氮采,即:send方法還沒有被調(diào)用,依然可以修改請求頭");
}
if (xhr.readyState === 2) {
//如果在這里修改請求頭會報(bào)錯
console.log("請求已經(jīng)發(fā)出去了染苛,即:send方法已經(jīng)被調(diào)用了鹊漠,不能再修改請求頭,響應(yīng)首行和響應(yīng)頭已經(jīng)回來了");
console.log(xhr.getResponseHeader("Date"));//Wed, 14 Oct 2020 12:28:58 GMT
}
if (xhr.readyState === 3) {
console.log("數(shù)據(jù)回來了茶行,但是數(shù)據(jù)可能不完整躯概,如果數(shù)據(jù)小鹰椒,會在此階段直接接受完畢畏纲,數(shù)據(jù)大則有待于進(jìn)一步接受");
console.log(xhr.response);//get請求發(fā)回的數(shù)據(jù)
}
if (xhr.readyState === 4) {
console.log("數(shù)據(jù)完全回來了");
}
};
2.原生Ajax發(fā)送post請求
步驟
- 實(shí)例化一個XMLHttpRequest對象
- 給對象綁定一個事件監(jiān)聽
- 指定發(fā)送請求的方式蒜埋,地址吗铐,參數(shù)
- 設(shè)置post請求所特有的請求頭
- 發(fā)送請求
btn.onclick=function(){
//1.實(shí)例化一個XMLHttpRequest對象
const xhr=new XMLHttpRequest();
//2.給對象綁定一個事件監(jiān)聽
xhr.onreadystatechange=function(){
if(xhr.readyState===4&&xhr.status===200){
console.log(xhr.response);
let demo=document.getElementById("demo");
demo.innerHTML=xhr.response;
}
};
//3.指定發(fā)送請求的方式戳吝,地址乌叶,參數(shù)
xhr.open("POST","http://localhost:3000/test_post");
//4.設(shè)置post請求所特有的請求頭
xhr.setRequestHeader("content-type","application/x-www-form-urlencoded");
//5.發(fā)送請求
xhr.send("name=dexter&&age=20");
};
post請求與get請求的區(qū)別是post請求需要設(shè)置特殊的請求頭否副,在發(fā)送數(shù)據(jù)時(shí)是在send中發(fā)送而get請求則是以urlencoded編碼形式發(fā)送壹粟。
3.IE中存在的問題
對于chrome和火狐來說伯铣,當(dāng)請求地址不發(fā)生變化時(shí)呻此,瀏覽器會嘗試走協(xié)商緩存。但是對于IE只要請求的地址不發(fā)生變化腔寡,會直接走強(qiáng)緩存趾诗,這樣會導(dǎo)致服務(wù)端修改了代碼但是瀏覽器刷新不出來效果的情況。因此在開發(fā)中為了應(yīng)對IE的強(qiáng)緩存蹬蚁,會在請求參數(shù)中加一個無關(guān)的時(shí)間戳參數(shù)恃泪。
const xhr=new XMLHttpRequest();
xhr.onreadystatechange=function(){
if(xhr.readyState===4&&xhr.status===200){
console.log(xhr.response);
let demo=document.getElementById("demo");
demo.innerHTML=xhr.response;
}
};
xhr.open("GET","http://localhost:3000/test_get?name=dexter&age=20&t="+Date.now());
xhr.send();
4.使用promise封裝原生Ajax
- 定義發(fā)送Ajax發(fā)送請求的模板函數(shù)
function sendAjax(url,method,data){
return new Promise((resolve,reject)=>{
//1.創(chuàng)建xhr對象
let xhr=new XMLHttpRequest();
//2.綁定監(jiān)聽
xhr.onreadystatechange=function(){
if(xhr.readyState!==4){
return
}
if(xhr.readyState===4&&(xhr.status>=200&&xhr.status<300)){
const responseObj={
data:xhr.response,
status:xhr.status,
statusText:xhr.statusText
};
resolve(responseObj);
}else{
reject(new Error("請求出錯了"));
}
};
//3.將傳遞過來的數(shù)據(jù)加工成urlencoded形式的編碼
let dataKeys=Object.keys(data);//將對象的鍵封裝成數(shù)組的形式
/* Array(3)
0: "m"
1: "n"
2: "r"
length: 3 */
let str=dataKeys.reduce(function(pre,now){
return pre+=`${now}=${data[now]}&`;
},""); //m=1&n=2&r=4&
//4.發(fā)送請求
if(method.toLowerCase()==="get"){
url+=`?${str}`;
xhr.open(method,url);
xhr.send();
}else if(method.toLowerCase()==="post"){
xhr.open(method,url);
xhr.setRequestHeader("content-type","application/x-www-form-urlencoded");
xhr.send(str);
}
})
}
- 發(fā)送get請求
obj1={
m:1,
n:2,
r:4
};
btn1.onclick=function(){
sendAjax("http://localhost:3000/test_get","GET",obj1).then((data)=>{
console.log(data);
}).catch((err)=>{
console.log(err);
})
};
- 發(fā)送post請求
obj2={
name:"dexter",
age:25
}
btn2.onclick=function(){
sendAjax("http://localhost:3000/test_post","POST",obj2).then((data)=>{
console.log(data);
}).catch((err)=>{
console.log(err);
})
};
5.服務(wù)端代碼
const { request, response } = require("express");
let express=require("express");
let app=express();
//暴露靜態(tài)資源
app.use(express.static(__dirname+"/public"));
//解析psot請求請求體中的urlencoded形式的參數(shù)
app.use(express.urlencoded({extended:true}));
app.get("/test_get",(request,response)=>{
console.log("一個get請求來了",request.query);
response.send("get請求發(fā)回的數(shù)據(jù)");
})
app.post("/test_post",(request,response)=>{
console.log(request.body);
console.log("一個post請求來了");
response.send("post請求發(fā)回的數(shù)據(jù)");
})
app.listen(3000,(err)=>{
if(!err){
console.log("服務(wù)器已啟動,3000端口正在監(jiān)聽");
}else{
console.log(err);
}
})
三犀斋、取消上一次請求
問題描述:
當(dāng)用戶點(diǎn)擊鼠標(biāo)發(fā)送一次請求在沒有得到服務(wù)器響應(yīng)之前再次點(diǎn)擊了請求贝乎,或者連續(xù)多次發(fā)送請求,這樣會導(dǎo)致服務(wù)器在反映過來后向用戶多次返回相同的數(shù)據(jù)叽粹,造成用戶體驗(yàn)較差览效。因此應(yīng)當(dāng)對這樣的情況進(jìn)行處理,即當(dāng)用戶連續(xù)多次的發(fā)送同樣的請求時(shí)即使服務(wù)器當(dāng)下處理不過來虫几,在反應(yīng)過來時(shí)也只給用戶返回最后一次請求的數(shù)據(jù)锤灿。
let lastxhr
btn.onclick = function () {
if (lastxhr) {
lastxhr.abort();
/* abort()的工作方式
當(dāng)用戶點(diǎn)擊的速度一般時(shí),abort會追回用戶的請求辆脸,服務(wù)器不會接收到用戶的請求
當(dāng)用戶點(diǎn)擊的速度非车#快時(shí)以至于abort追不上時(shí),abort會攔截服務(wù)器返回的數(shù)據(jù) */
}
lastxhr = getcode();
};
function getcode() {
let xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && (xhr.status >= 200 && xhr.status < 300)) {
console.log(xhr.response);
}
};
xhr.open("GET", "http://localhost:3000/get_code");
xhr.send();
return xhr;
}
服務(wù)端代碼
app.get("/get_code",(request,response)=>{
//返回一個1000-9999的數(shù)
console.log("頁面請求驗(yàn)證碼");
let code=Math.floor(Math.random()*8999+1000);
setTimeout(()=>{
response.send(code.toString());//如果返回一個純數(shù)字服務(wù)器會以為返回的是狀態(tài)碼而報(bào)錯
},2000)
})
四啡氢、使用jQuery封裝Ajax
1.jQuery發(fā)送get請求
-
完整寫法中常用的幾個參數(shù)
--method: 發(fā)送請求的方式
--data: 要傳遞的數(shù)據(jù)
--success: 成功的回調(diào)
--error: 失敗的回調(diào)
let btn1 = $("#btn1");
btn1.click(function () {
$.ajax("http://localhost:3000/test_get",{
method:"GET",
data:{
name:"dexter",
age:25
},
success:function(result){
console.log(result);
},
error:function(err){
console.log(err);
}
})
})
-
精簡寫法的參數(shù)
精簡的寫法直接傳入請求地址和請求參數(shù)以及一個回調(diào)函數(shù)
這個回調(diào)函數(shù)會有三個形參:
第一個形參表示返回來的數(shù)據(jù)
第二個參數(shù)表示當(dāng)前請求的狀態(tài)
第三個參數(shù)是內(nèi)部使用的xhr對象
但是一般在使用時(shí)只傳進(jìn)去一個參數(shù)状囱,即服務(wù)器返回來的數(shù)據(jù)
$.get("http://localhost:3000/test_get", { name: "dexter", age: 25 }, (data) => {
console.log(data);
})
2.jQuery發(fā)送post請求
- 完整寫法
btn2.click(function () {
$.ajax("http://localhost:3000/test_post",{
method:"POST",
data:{
name:"dexter",
age:25
},
success:function(result){
console.log(result);
},
error:function(err){
console.log(err);
}
})
})
- 精簡寫法
$.post("http://localhost:3000/test_post", { name: "dexter", age: 25 }, (data) => {
console.log(data);
})