傳統網站中存在的問題
- 網速慢的情況下,頁面加載時間長,用戶只能等待
- 表單提交後,如果一項內容不合格,需要重新填寫所有表單內容
- 頁面跳轉,重新加載頁面,造成資源浪費,增加用戶等待時間
Ajax :它是瀏覽器提供的一套方法,可以實現頁面無刷新更新數據,提高用戶瀏覽網站應用的體驗。
Ajax 運行原理及實現
Ajax 運行原理
Ajax 相當於瀏覽器發送請求與接收響應的代理人,以實現在不影響用戶瀏覽頁面的情況下,局部更新頁面數據,從而提高用戶體驗。
Ajax的實現步驟
<script type="text/javascript">
//1、創建ajax對象
const xhr=new XMLHttpRequest();
//2、添加請求方式
xhr.open('get','http://localhost:3000/first')
//3、發送請求
xhr.send();
//4、接收服務端返回給客戶端的結果
//接收完響應自動觸發事件
xhr.onload=function(){
//xhr.responseText獲取服務端的響應數據
console.log(xhr.responseText);
}
</script>
//服務端
app.get('/first',(req,res)=>{
res.send('我響應了ajax的請求')
})
服務端響應的數據格式
在 http 請求與響應的過程中,無論是請求參數還是響應內容,
如果是對象類型,最終都會被轉換爲對象字符串進行傳輸。
JSON.parse() // 將 json 字符串轉換爲json對象
請求參數傳遞
傳統網站表單提交
<form method="get" action="http://www.example.com">
<input type="text" name="username"/>
<input type="password" name="password">
</form>
<!– http://www.example.com?username=zhangsan&password=123456 -->
Ajax傳遞方式
GET 請求方式
xhr.open('get', 'http://www.example.com?name=zhangsan&age=20');
//get請求方式將請求的參數放在url後面
POST 請求方式
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded') xhr.send('name=zhangsan&age=20');
//post請求方式把請求的參數放置在請求體中進行傳遞
請求報文
在 HTTP 請求和響應的過程中傳遞的數據塊就叫報文,包括要傳送的數據和一些附加信息,這些數據和信息要遵守規定好的格式。
請求參數的格式
1、application/x-www-form-urlencoded
name=zhangsan&age=20&sex=男
2、application/json
{name: 'zhangsan', age: '20', sex: '男'}
// 在請求頭中指定 Content-Type 屬性的值是 application/json,告訴服務器端當前請求參數的格式是 json
JSON.stringify() // 將json對象轉換爲json字符串
注意:get 請求是不能提交 json 對象數據格式的,傳統網站的表單提交也是不支持 json 對象數據格式的。
獲取服務器端的響應
Ajax 狀態碼
在創建ajax對象,配置ajax對象,發送請求,以及接收完服務器端響應數據,這個過程中的每一個步驟都會對應一個數值,這個數值就是ajax狀態碼。
0:請求未初始化(還沒有調用open())
1:請求已經建立,但是還沒有發送(還沒有調用send())
2:請求已經發送
3:請求正在處理中,通常響應中已經有部分數據可以用了
4:響應已經完成,可以獲取並使用服務器的響應了
xhr.readyState // 獲取Ajax 狀態碼
onreadystatechange 事件
當 Ajax 狀態碼發生變化時將自動觸發該事件
在事件處理函數中可以獲取 Ajax 狀態碼並對其進行判斷,當狀態碼爲 4 時就可以通過 xhr.responseText 獲取服務器端的響應數據了.
// 當Ajax狀態碼發生變化時
xhr.onreadystatechange = function () {
// 判斷當Ajax狀態碼爲4時
if (xhr.readyState == 4) {
// 獲取服務器端的響應數據
console.log(xhr.responseText);
}
}
//推薦使用onload事件、不需要兼容、不需要判斷ajax狀態碼、不被調用多次
Ajax 錯誤處理
1. 網絡暢通,服務器端能接收到請求,服務器端返回的結果不是預期結果。
可以判斷服務器端返回的狀態碼,分別進行處理。xhr.status 獲取http狀態碼
2. 網絡暢通,服務器端沒有接收到請求,返回404狀態碼。
檢查請求地址是否錯誤。
3. 網絡暢通,服務器端能接收到請求,服務器端返回500狀態碼。
服務器端錯誤,
4. 網絡中斷,請求無法發送到服務器端。
會觸發xhr對象下面的onerror事件,在onerror事件處理函數中對錯誤進行處理
低版本 IE 瀏覽器的緩存問題
問題:在低版本的 IE 瀏覽器中,Ajax 請求有嚴重的緩存問題,即在請求地址不發生變化的情況下,
只有第一次請求會真正發送到服務器端,後續的請求都會從瀏覽器的緩存中獲取結果。
即使服務器端的數據更新了,客戶端依然拿到的是緩存中的舊數據。
解決方案:在請求地址的後面加請求參數,保證每一次請求中的請求參數的值不相同。
xhr.open('get', 'http://www.example.com?t=' + Math.random());
Ajax 異步編程
同步異步概述
同步()
- 一個人同一時間只能做一件事情,只有一件事情做完,才能做另外一件事情。
- 落實到代碼中,就是上一行代碼執行完成後,才能執行下一行代碼,即代碼逐行執行
console.log('before');
console.log('after');
異步()
-個人一件事情做了一半,轉而去做其他事情,當其他事情做完以後,再回過頭來繼續做之前未完成的事情。
-落實到代碼上、就是異步代碼雖然需要花費時間去執行,但程序不會等待異步代碼執行完成後再繼續執行後續代碼,
-而是直接執行後續代碼,當後續代碼執行完成後再回頭看異步代碼是否返回結果,
-如果已有返回結果,再調用事先準備好的回調函數處理異步代碼執行的結果。
console.log('before');
setTimeout(
() => { console.log('last');
}, 2000);
console.log('after');
Ajax 封裝
問題
發送一次請求代碼過多,發送多次請求代碼冗餘且重複
將請求代碼封裝到函數中,發請求時調用函數即可
<script>
//ajax封裝
function ajax(options) {
//存儲默認值
var defaults={
type:'get',
url:'',
data:{},
header:{
'Content-Type':'application/x-www-form-urlencoded'
},
success:function(){},
error:function(){}
}
//使用options覆蓋defaults
Object.assign(defaults,options);
//創建ajax對象
var xhr = new XMLHttpRequest();
//設置拼接請求參數變量
var params = '';
for (attr in defaults.data) {
//拼接參數、爲字符串格式
params += attr + '=' + defaults.data[attr] + '&';
}
//將參數最後面的&符號截取掉、並重新賦值
params = params.substr(0, params.length - 1)
//判斷請求方式
if (defaults.type == 'get') {
//如果請求參數是get就拼接到參數的請求路徑後面
defaults.url = defaults.url + "?" + params;
}
//配置ajax
xhr.open(defaults.type, defaults.url);
//如果請求參數爲post
if (defaults.type == 'post') {
//用戶希望向服務器端傳遞的請求參數的類型
var contentType = defaults.header['Content-Type']
//明確post的請求類型
xhr.setRequestHeader('Content-Type', contentType);
if (contentType == 'application/json') {
//是post參數直接放在請求體中
xhr.send(JSON.stringify(defaults.data));
} else {
//發送請求
//向服務器端傳遞普通類型的請求參數
xhr.send(params);
}
}else{
//發送請求
xhr.send();
}
//接收完響應數據後觸發
xhr.onload = function () {
//獲取響應頭中的數據
var contentType= xhr.getResponseHeader('Content-Type');
//服務器端返回的數據
var responseText=xhr.responseText;
//判斷字符串是否包含某一個字符
if(contentType.includes('application/json')){
responseText=JSON.parse(responseText)
}
if(xhr.status==200){
//調用請求成功的函數
defaults.success(responseText,xhr);
}else{
//獲取調用失敗的函數
defaults.error(responseText,xhr)
}
}
}
ajax({
// type:"post",
//請求地址
url: 'http://localhost:3000/responseData',
//回調函數、返回響應信息
success: function (data) {
console.log('這裏是success函數');
console.log(data);
}
})
//服務器的路由
app.get('/responseData',(req,res)=>{
res.send({"name":"zs" });
})
</script>