AJAX基礎知識及核心原理解讀
AJAX基礎知識
什麼是AJAX?
async javascript and xml 非同步的JS和XML
XML:可擴充套件的標記語言
作用:是用來儲存資料的(通過自己擴充套件的標記名稱清晰地展示出資料結構)
ajax之所以稱為非同步的js和xml,主要原因是:以前最開始使用ajax實現客戶端和服務端資料通訊的時候,傳輸資料的格式一般都是xml格式的資料,我們把他稱之為非同步的js和xml(現在一般都是基於JSON格式來進行資料傳輸的)
<?xml version="1.0" encoding="UTF-8"?>
<root>
<student>
<name>海洋</name>
<age>10</age>
<score>
<deutsch>100</deutsch>
<IT>100</IT>
<english>100</english>
</score>
</student>
</root>
複製程式碼
非同步的JS
這裡的非同步不是說ajax只能基於非同步進行請求(雖然建議都是使用非同步程式設計),這裡的非同步特指的是 區域性重新整理
區域性重新整理 VS 全域性重新整理
全域性重新整理:
在非完全前後端分離的專案中,前端開發只需要完成頁面的製作,並且把一些基礎的人機互動效果使用js完成即可,頁面中需要動態呈現內容的部分,都是交給後臺開發工程師做資料繫結和基於伺服器進行渲染的(伺服器端渲染)
非完全前後端分離:
【優勢】
- 動態展示的資料在頁面的原始碼中可以看見,有利於SEO優化推廣(有利於搜尋引擎的收錄和抓取)
- 從伺服器端獲取的結果已經是解析渲染完成的了,不需要客戶端再去解析渲染了,所以頁面載入速度快(前提是伺服器端處理的速度夠快,能夠處理過來),所以類似於京東,淘寶這些網站,首屏資料一般都是由伺服器端渲染的
【弊端】
- 如果頁面中存在實時更新的資料,每一次想要展示最新的資料,頁面都要重新重新整理一次,這樣肯定不行,非常耗效能
- 都交給伺服器端做資料渲染,伺服器端的壓力太大,如果伺服器處理不過來,頁面呈現的速度更慢(所以像京東和淘寶這類的網站,除了首屏是伺服器端渲染的,其他屏一般都是客戶端做資料渲染繫結的)
- 這種模式不利於開發,(開發效率低)
區域性重新整理
目前市場上大部分專案都是前後端完全分離的專案(也有非完全前後端分離的佔少數)
前後端完全分離的專案,頁面中需要動態繫結的資料是交給客戶端完成渲染的
- 向伺服器端傳送AJAX請求
- 把從伺服器端獲取的資料解析處理,拼接成我們需要展示的HTML字串
- 把拼接好的字串替換頁面中的某一部分內容(區域性重新整理),頁面不需要整體重新載入,區域性渲染即可
前後端完全分離:
【優勢】
我們可以根據需求,任意修改頁面中的某一部分內容(例如實時重新整理),整體頁面不重新整理,效能好,體驗好(所有表單驗證,需要實時重新整理的等需求都要基於AJAX實現)
有利於開發,提高開發效率
1)前後端的完全分離,後臺不需要考慮前端如何實現,前端也不需要考慮後臺用什麼技術,真正意義上實現了技術的劃分
2)可以同時進行開發:專案開發開始,首先制定前後端資料互動的介面文件(文件中包含了,調取哪個介面或者哪些資料等協議規範),後臺把介面先寫好(目前很多公司也需要前端自己拿NODE來模擬這些介面),客戶端按照介面調取即可,後臺再去實現介面功能即可
【弊端】
- 不利於SEO優化:第一次從伺服器端獲取的內容不包含需要動態繫結的資料,所以頁面的原始碼中沒有這些內容,不利於SEO收錄,後期通過JS新增到頁面中的內容,並不會寫在頁面的原始碼中(是原始碼不是頁面結構)
- 交由客戶端渲染,首先需要把頁面呈現,然後在通過JS的非同步AJAX請求獲取資料,在進行資料繫結,瀏覽器再把動態增加的部分重新渲染,無形中浪費了一些時間,沒有伺服器端渲染頁面呈現速度快
基於原生JS實現AJAX
// 建立一個AJAX物件
let xhr = new XMLHttpRequest(); // 不相容IE6及更低版本瀏覽器(IE6:ActiveXObject)
// 開啟請求地址(可以理解為一些基礎配置,但是並沒有傳送請求呢)
xhr.open([method],[url],[async],[userName],[passWord]);
// 監聽AJAX狀態改變,獲取響應資訊(獲取響應頭資訊,獲取響應主體資訊)
xhr.onreadystatechange = ()=>{
if(xhr.readyState === 4 && xhr.status === 200){
let result = xhr.responseText; // 獲取響應主體中的內容
}
};
// 傳送AJAX請求(括號中傳遞的內容就是請求主體的內容)
xhr.send(null);
複製程式碼
分析第二步中的細節點
xhr.open([method],[url],[async],[userName],[passWord])
[AJAX請求方式]
- GET請求系列(獲取)
- get
- delete:從伺服器上刪除某些資原始檔
- head:只想獲取伺服器返回的響應頭資訊(響應主體不需要獲取)
- POST請求系列(推送)
- post
- put:向伺服器中增加指定的資原始檔
不管哪一種請求方式,客戶端都可以把資訊傳遞給伺服器端,伺服器端也可以把資訊返回給客戶端,只是GET系列一般以獲取為主(給的少,拿回來的多),而POST系列一般以推送為主(給的多,拿回來的少)
1)我們想要獲取一些動態展示的資訊,一般使用GET請求,因為只需要向伺服器端傳送請求,告訴伺服器端我們想要什麼,伺服器端就會把需要的資料返回
2)在實現註冊功能的時候,我們需要把客戶輸入的資訊傳送給伺服器進行儲存,伺服器一般返回成功或失敗等狀態,此時我們一般都是基於POST請求完成的
GET系列請求和POST系列請求,在專案實戰中存在很多的區別
GET請求傳遞給伺服器的內容一般沒有POST請求傳遞給伺服器的內容多
原因:GET請求傳遞給伺服器內容一般都是基於 URL地址問號傳遞引數 來實現的,而POST請求一般都是基於 設定請求主體 來實現的,各個瀏覽器都有URL最大長度的限制(谷歌:8kb,火狐:7kb,IE:2kb),超出長度部分,瀏覽器會自動擷取掉,導致傳遞給伺服器的資料缺失
理論上POST請求通過請求主體傳遞是沒有大小限制的,真實專案中為了保證傳輸的速率,我們也會限制大小(例如:上傳的資料或者圖片我們會做大小的限制)
GET請求很容易出現快取(這個快取不可控,一般我們都不需要),而POST不會出現快取(除非自己做特殊處理)
原因:GET是通過URL問號傳參傳遞給伺服器資訊,會出現快取;而POST是設定請求主體,不會出現快取。
// 每隔一分鐘重新請求伺服器端最新的資料,然後展示在頁面中(頁面中某些資料實時重新整理)
setTimeout(()=>{
$.ajax({
url: 'getList?lx = news',
...
success: result=>{
// 第一次請求資料回來,間隔一分鐘後,瀏覽器又傳送一次請求,但是新傳送的請求不論是地址還是傳遞的引數都和第一次一模一樣,瀏覽器很有可能會把上一次的資料獲取,而不是獲取最新的資料
}
})
},600)
// 解決方案:在每一次重新請求的時候,在URL的末尾追加一個隨機數,保證每一次請求的地址不完全一致,就可以避免是從快取中讀取的資料
setTimeout(()=>{
$.ajax({
url: 'getList?lx = news&_=' + Math.random(),
...
success: result=>{
}
})
},600)
複製程式碼
GET請求沒有POST請求安全(POST也並不是十分安全,只是相對安全)
原因:還是因為GET是URL傳參給伺服器
有一種比較簡單的黑客技術:URL劫持,也就是可以把客戶端傳遞給伺服器的資料劫持到,導致資訊洩露
URL:請求資料的地址(API地址),在真實專案中,後臺開發工程師會編寫一個API文件,在API文件中彙總了獲取哪些資料需要使用哪些地址,我們按照文件操作即可
ASYNC:非同步(SYNC 同步),設定當前AJAX請求是非同步的還是同步的,不寫預設是非同步(TRUE),如果設定為FALSE,則代表當前請求是同步的
使用者名稱和密碼:這兩個引數一般不用,如果你請求的URL地址所在的伺服器設定了訪問許可權,則需要我們提供可通行的使用者名稱和密碼才可以(一般伺服器都是可以匿名訪問的)
分析第三步中的細節點
AJAX狀態碼:描述當前AJAX操作狀態的:
xhr.readyState
0:UNSENT未傳送,只要建立一個AJAX物件,預設值就是零
1:OPENED 我們已經執行了xhr.open這個操作
2:HEADERS_RECEIVED 當前AJAX的請求已經傳送,並且已經接收到伺服器端返回的響應頭資訊了
3:LOADING 響應主體的內容正在返回的路上
4:DONE 響應主體內容已經返回到客戶端
HTTP網路狀態碼:記錄了當前伺服器返回資訊的狀態 :
xhr.status
200:成功,一個完整的HTTP事務完成(以2開頭的狀態碼一般都是成功)
以3開頭一般也是成功,只不過伺服器端做了很多處理
301:Moved Permanently 永久轉移(永久重定向),一般應用於域名遷移
302:Move temporarily 臨時轉移 (臨時重定向,新的HTTP版本中任務307是臨時重定向),一般用於伺服器的負載均衡:當前伺服器處理不過來,把當前請求臨時交給其他的伺服器處理(一般圖片請求經常出現302,很多公司都有單獨的圖片伺服器)
304:Not Modified 從瀏覽器快取中獲取資料。把一些不經常更新的檔案或者內容快取到瀏覽器中,下一次從快取中獲取,減輕伺服器壓力,也提高頁面載入速度
以4開頭的,一般都是失敗,而且客戶端的問題偏大
400:請求引數錯誤
401:無許可權訪問
404:訪問地址不存在
以5開頭的,一般都是失敗,而且伺服器端的問題偏大
500:Internal Server Error 未知的伺服器錯誤
503:Service Unavailable 伺服器超負載
AJAX中其他常用的屬性和方法
面試題:AJAX中總共支援幾個方法?
let xhr = new XMLHttpRequest();
console.dir(xhr);
//【屬性】
// 1.readyState:儲存的是當前AJAX的狀態碼
// response/responseText/responseXML:都是用來接收伺服器返回的響應主體中的內容,只是根據伺服器返回內容格式的不一樣,我們使用不同的屬性接收即可
// responseText是最常用的,接收的結果是字串格式的(一般伺服器返回的資料都是JSON格式字串)
// responseXML偶爾會用到,如果伺服器返回的是XML文件資料,我們需要使用這個屬性接收
// status:記錄了伺服器端返回的HTTP狀態碼
// statusText:對返回的狀態碼的描述
// timeout:設定當前AJAX請求的超時時間,假設我們設定時間為3000ms,從AJAX 請求傳送開始,3秒後響應主體內容還沒有返回,瀏覽器會把當前AJAX請求任務強制斷開
// 【方法】
// abort():強制中斷AJAX請求
// getAllResponseHeaders():獲取全部的響應頭資訊(獲取的結果是一堆字串文字)
// getResponseHeader(key):獲取指定屬性名的響應頭資訊,例如:xhr.getResponseHeader('date')獲取響應頭中儲存的伺服器的時間
// open():開啟一個URL地址
// overrideMimeType():重寫資料的MIME型別
// send():傳送AJAX請求(括號中寫的是客戶端基於請求主體把資訊傳遞給伺服器)
// setRequestHeader(key,value):設定請求頭資訊(可以是設定自定義請求頭資訊)
// [事件]
// onabort:當AJAX被中斷請求時觸發這個事件
// onreadystatechange:AJAX狀態發生改變會觸發這個事件
// ontimeout:當AJAX請求超時,會觸發這個事件
複製程式碼
例子:
let xhr = new XMLHttpRequest();
xhr.open('get','temp.json?_=' + Math.random(), true);
xhr.setRequestHeader('aaa', '123'); // 注意:請求頭部的內容不得出現中文漢字。設定請求頭資訊必須在OPEN之後和SEND之前
// 設定超時
xhr.timeout = 10;
xhr.ontimeout = ()=>{
console.log('當前請求已經超時');
xhr.abort();
};
xhr.onreadystatechange = ()=>{
let {readyState:state, status} = xhr;
if(!/^(2|3)\d{2}$/.test(status))
return;
// 在狀態為2的時候就可以獲取響應頭資訊
if (state === 2){
let headerAll = xhr.getAllResponseHeaders(),
serverDate = xhr.getResponseHeader('date'); // 獲取的伺服器時間是格林尼治時間(相比北京時間差了8小時),通過new Date 可以把這個時間轉換為北京時間
console.log(headerAll, new Date(serverDate);
return;
}
// 在狀態為4的時候響應主體內容就已經回來了
if (state === 4){
let valueText = xhr.responseText, // 獲取到的結果一般都是JSON字串(可以使用JSON.PARSE把其轉換為JSON物件)
valueXML = xhr.responseXML; // 獲取到的結果是XML格式的資料(可以通過XML的一些常規操作獲取儲存的指定資訊)
// 如果伺服器返回的是XML文件,用responseText獲取的結果是字串,而用responseXML獲取的是標準XML文件
console.log(valueText, valueXML);
}
};
xhr.send('name=hy&age=6&sex=man');
複製程式碼
JS中常用的編碼和解碼方法
正常的編碼解碼(非加密)
- escape / unescape: 主要就是把中文漢字進行編碼和解碼(一般只有JS語言支援:也經常應用於前端頁面通訊時候的中文漢字編碼)
str = '你好海洋 哈哈'
"你好海洋 哈哈"
escape(str)
"%u4F60%u597D%u6D77%u6D0B%20%u54C8%u54C8"
unescape("%u4F60%u597D%u6D77%u6D0B%20%u54C8%u54C8")
"你好海洋 哈哈"
複製程式碼
- encodeURI / decodeURI : 基本上所有的程式語言都支援
str = '你好海洋 哈哈'
"你好海洋 哈哈"
encodeURI(str)
"%E4%BD%A0%E5%A5%BD%E6%B5%B7%E6%B4%8B%20%E5%93%88%E5%93%88"
decodeURI("%E4%BD%A0%E5%A5%BD%E6%B5%B7%E6%B4%8B%20%E5%93%88%E5%93%88")
"你好海洋 哈哈"
複製程式碼
encodeURIComponent / decodeURIComponent 和第二種方式非常類似,區別在於:
當我們通過URL問號傳參的時候,我們傳遞的引數值還是一個URL或者包含很多的特殊字元,此時為了不影響主要的URL,我們需要把傳遞的引數值進行編碼,使用encodeURI不能編碼一些特殊字元,所以只能使用恩codeURLComponent處理
str = '你好海洋 哈哈'
"你好海洋 哈哈"
encodeURIComponent(str)
"%E4%BD%A0%E5%A5%BD%E6%B5%B7%E6%B4%8B%20%E5%93%88%E5%93%88"
decodeURIComponent("%E4%BD%A0%E5%A5%BD%E6%B5%B7%E6%B4%8B%20%E5%93%88%E5%93%88")
"你好海洋 哈哈"
複製程式碼
let str = 'http://wudiufo.github.io/?',
obj = {
name:'haiyang',
age:'8';
url:'http://www.haiyang.com/?lx=1'
};
// 需求:把obj中的每一項屬性名和屬性值拼接到URL末尾,通過問號傳參的方式
for(let key in obj){
str+=`${key}=${encodeURIComponent(obj[key])}&`;
// 不能使用encodeURI,必須使用encodeURIComponent,原因是encodeURI不能編碼特殊的字元
}
console.log(str.replace(/&$/g), '');
// 後期獲取URL問號引數的時候,我們把獲取的值在依次的解碼即可
String.prototype.myQueryUrlParameter=function myQueryUrlParameter(){
let reg=/[?&]([^?&=]+)(?:=([^?&=]*))?/g,
obj={};
this.replace(reg,(...arg)=>{
let [,key,value]=arg;
obj[key]=decodeURIComponent(value); //此處獲取的時候可以進行解碼
});
return obj;
}
複製程式碼
也可以通過加密的方法進行編碼解碼
可逆轉加密(一般都是團隊自己用的規則)
不可逆轉加密(一般都是基於MD5完成的,可能會把MD5加密後的結果二次加密)
AJAX中的同步和非同步程式設計
AJAX這個任務:傳送請求接收到響應主體內容(完成一個完整的HTTP事務)
xhr.send() : 任務開始
xhr.readState===4 : 任務結束
let xhr = new XMLHttpRequest();
xhr.open('get', 'temp.json', false);
xhr.onreadystatechange = () =>{
console.log(xhr.readyState);
};
xhr.send();
// 只輸出一次結果是4
複製程式碼
let xhr = new XMLHttpRequest();
xhr.open('get', 'temp.json', false);
xhr.send(); // [同步]開始傳送AJAX請求,開啟AJAX任務,在任務沒有完成之前,什麼事情都做不了(下面繫結事件也做不了)=》loading =》當readyState===4的時候AJAX任務完成,開始執行下面的操作
// readyState===4
xhr.onreadystatechange = () =>{
console.log(xhr.readyState);
};
// 繫結方法之前狀態已經為4了,此時AJAX的狀態不會再改變成其他的值了,所以事件永遠都不會被觸發,一次都沒執行方法(使用AJAX同步程式設計,不要把send放在事件監聽前,這樣我們無法再繫結的方法中獲取到響應主體的內容)
複製程式碼
let xhr = new XMLHttpRequest();
xhr.open('get', 'temp.json');
xhr.onreadystatechange = () =>{
console.log(xhr.readyState);
};
xhr.send();
// 輸出3次,結果分別是2,3 ,4
複製程式碼
let xhr = new XMLHttpRequest();
xhr.open('get', 'temp.json');
xhr.send();
// xhr.readyState===1
xhr.onreadystatechange = () =>{
console.log(xhr.readyState);
};
// 2,3,4
複製程式碼
let xhr = new XMLHttpRequest();
xhr.onreadystatechange = () =>{
console.log(xhr.readyState);
};
xhr.open('get', 'temp.json');
xhr.send();
// 1,2,3,4
複製程式碼
let xhr = new XMLHttpRequest();
// xhr.readyState===0
xhr.onreadystatechange = () =>{
console.log(xhr.readyState);
};
xhr.open('get', 'temp.json',false);
// xhr.readyState===1,AJAX特殊處理了一件事,執行OPEN狀態變為1,會主動把之前監聽的方法執行一次,然後再去執行SEND
xhr.send();
// xhr.readyState===4 AJAX任務結束,住任務佇列完成
// 1,4
複製程式碼
實戰案例:倒數計時搶購
1,結構:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="box"></div>
<script src="./1.js"></script>
</body>
</html>
複製程式碼
2, 互動:
~ function() {
let box = document.getElementById('box');
let serverTime = null;
let fn = () => {
// 1,計算當前時間和目標時間的差值
// new Date():獲取的是客戶端本機時間(會受到客戶端自己調整時間的影響),重要的時間參考不能基於這個完成,不論哪一個客戶端都要基於相同的伺服器時間計算
// 每間隔一秒鐘,我們需要把第一次獲取的伺服器時間進行累加
serverTime = serverTime + 1000;
let tarTime = new Date('2018/12/22 12:00:00').getTime(),
spanTime = tarTime - serverTime;
// 2,計算差值中包含多少時分秒
if (spanTime < 0) {
// 已經錯過了搶購時間(已經開搶了)
box.innerHTML = '開槍';
clearInterval(autoTimer);
return;
}
let hours = Math.floor(spanTime / (1000 * 60 * 60));
spanTime -= hours * 3600000;
let minus = Math.floor(spanTime / (1000 * 60));
spanTime -= minus * 60000;
let seconds = Math.floor(spanTime / 1000);
hours < 10 ? hours = '0' + hours : null;
minus < 10 ? minus = '0' + minus : null;
seconds < 10 ? seconds = '0' + seconds : null;
box.innerHTML = `距離開槍還剩下 ${hours}:${minus}:${seconds}`;
};
let autoTimer = setInterval(fn, 1000);
// 從伺服器端獲取伺服器時間
let getServerTime = () => {
let xhr = new XMLHttpRequest();
xhr.onreadystatechange = () => {
// console.log(xhr.readyState);
// HEAD請求方式,狀態碼中沒有3(不需要等待響應主體內容)
if (!/^(2|3)\d{2}$/.test(xhr.status)) return;
if (xhr.readyState === 2) {
serverTime = new Date(xhr.getResponseHeader('date')).getTime();
fn();
}
};
xhr.open('head', 'temp.xml', true);
xhr.send(null);
// 1,伺服器返回的時間在響應頭就有,我們只需要獲取響應頭資訊即可,沒必要獲取響應主體內容,所以請求方式使用HEAD即可
// 2,必須使用非同步程式設計:同步程式設計我們無法再狀態為2或3的時候做一些處理,而我們獲取響應頭資訊,在狀態為2的時候就可以獲取了,所以需要使用非同步
// 3,在狀態為2的時候就把伺服器時間獲取到
// 獲取伺服器時間總會出現時間差的問題,伺服器端把時間記錄好,到客戶端獲取到時間有延遲差(例如:伺服器返回的時候記錄的是10:00,到客戶端獲取的時候已經是10:01,但是客戶端獲取的結果依然是10:00,這樣就有了1秒鐘的時間差)【儘可能的減少時間差,是我們優化的全部目的】
};
getServerTime();
}();
複製程式碼
AJAX類庫的封裝
JQ中AJAX操作詳解
$.ajax({
url:'xxx.txt', //請求API地址
method:'get', //請求方式GET/POST...,在老版本JQ中使用的是type,使用type和method實現的是相同的效果
dataType:'json', //dataType只是我們預設獲取結果的型別,不會影響伺服器的返回,(伺服器端一般給我們返回的都是JSON格式的字串),如果我們預設的是JSON,那麼類庫中將把伺服器返回的字串轉換為JSON物件,如果我們預設的是text(預設值),我們把伺服器獲取的結果直接拿過來操作即可,我們預設的值還可以是xml等
cache:false, // 設定是否清除快取,只對GET系列請求有作用,預設是true,有快取,手動設定為false,無快取,JQ類庫會在請求URL的末尾追加一個隨機數來清除快取
data:null, //我們通過data可以把一些資訊傳遞給伺服器;GET系列請求會把data中的內容拼接在URL的末尾通過問號傳參的方式傳遞給伺服器,POST系列請求會把內容放在請求主體中傳遞給伺服器。data的值可以設定為兩種格式:字串,物件,如果是字串,設定的值是什麼傳遞給伺服器的就是什麼,如果設定的是物件,JQ會把物件變為 xxx=xxx&xxx=xxx 這樣的字串傳遞給伺服器
async:true,//設定同步或者非同步,預設是true代表非同步,false是同步
success:function(result){
// 當AJAX請求成功(readyState===4 & status是以2或者4開頭的)
// 請求成功後JQ會把傳遞的回撥函式執行,並且把獲取的結果當做實參傳遞給回撥函式(result就是我們從伺服器獲取的結果)
},
error:function(msg){}, // 請求錯誤觸發回撥函式
complete:function(){}, // 不論請求是錯誤的還是正確的都會觸發回撥函式(他是完成的意思)
})
複製程式碼
封裝屬於自己的AJAX類庫
【支援的引數】
url
method/type
data
dataType
async
cache
success
~ function() {
class ajaxClass {
// SEND AJAX
init() {
// THIS:EXAMPLE
let xhr = new XMLsHttpRequest();
xhr.onreadystatechange = () => {
if (!/[23]\d{2}$/.test(xhr.status)) return;
if (xhr.readyState === 4) {
let result = xhr.responseText;
// DATA-TYPE
switch (this.dataType.toUpperCase()) {
case 'TEXT':
case 'HTML':
break;
case 'JSON':
result = JSON.parse(result);
break;
case 'XML':
result = xhr.responseXML;
}
this.success(result);
};
};
// DATA
if (this.data != null) {
this.formatData();
if (this.isGET) {
this.url += this.querySymbol() + this.data;
this.data = null;
}
}
// CACHE
this.isGET ? this.cacheFn() : null;
xhr.open(this.method, this.url, this.async);
xhr.send(this.data);
}
// 把傳遞的物件格式data轉換為字串格式的data
formatData() {
// THIS:EXAMPLE
if (Object.prototype.toString.call(this.data) === '[object Object]') {
let obj = this.data,
str = ``;
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
str += `${key}=${obj[key]}&`;
}
}
str = str.replace(/&$/g, '');
this.data = str;
}
}
cacheFn() {
// THIS:EXAMPLE
!this.cache ? this.url += `${this.querySymbol()}_=${Math.random()}` : null;
}
querySymbol() {
// THIS:EXAMPLE
return this.querySymbol.indexof('?') > -1 ? '&' : '?';
}
}
// init parameters
window.ajax = function({
url = null,
method = 'GET',
type = 'null',
data = null,
dataType = 'JSON',
cache = true,
async = true,
success = null
} = {}) {
let example = new ajaxClass();
example.url = url;
example.method = type === null ? method : type;
example.data = data;
example.dataType = dataType;
example.cache = cache;
example.async = async;
example.success = typeof success === 'function' ? success : new Function();
example.isGET = /^(GET|DELETE|HEAD)$/i.test(example.method);
example.init();
return example;
};
}();
ajax({});
複製程式碼
優化程式碼:
~ function() {
class ajaxClass {
// SEND AJAX
init() {
// THIS:EXAMPLE
let xhr = new XMLHttpRequest();
xhr.onreadystatechange = () => {
if (!/[23]\d{2}$/.test(xhr.status)) return;
if (xhr.readyState === 4) {
let result = xhr.responseText;
// DATA-TYPE
try {
switch (this.dataType.toUpperCase()) {
case 'TEXT':
case 'HTML':
break;
case 'JSON':
result = JSON.parse(result);
break;
case 'XML':
result = xhr.responseXML;
}
} catch (e) {
}
this.success(result);
};
};
// DATA
if (this.data != null) {
this.formatData();
if (this.isGET) {
this.url += this.querySymbol() + this.data;
this.data = null;
}
}
// CACHE
this.isGET ? this.cacheFn() : null;
xhr.open(this.method, this.url, this.async);
xhr.send(this.data);
}
// 把傳遞的物件格式data轉換為字串格式的data
formatData() {
// THIS:EXAMPLE
if (Object.prototype.toString.call(this.data) === '[object Object]') {
let obj = this.data,
str = ``;
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
str += `${key}=${obj[key]}&`;
}
}
str = str.replace(/&$/g, '');
this.data = str;
}
}
cacheFn() {
// THIS:EXAMPLE
!this.cache ? this.url += `${this.querySymbol()}_=${Math.random()}` : null;
}
querySymbol() {
// THIS:EXAMPLE
return this.querySymbol.indexof('?') > -1 ? '&' : '?';
}
}
// init parameters
window.ajax = function({
url = null,
method = 'GET',
type = 'null',
data = null,
dataType = 'JSON',
cache = true,
async = true,
success = null
} = {}) {
let _this = new ajaxClass();
['url', 'method', 'data', 'dataType', 'cache', 'async', 'success'].forEach((item) => {
if (item === 'method') {
_this.method = type === null ? method : type;
return;
}
if (item === 'success') {
_this.success = typeof success === 'function' ? success : new Function();
return;
}
_this[item] = eval(item);
});
_this.isGET = /^(GET|DELETE|HEAD)$/i.test(_this.method);
_this.init();
return _this;
};
}();
ajax({});
複製程式碼