AJAX基礎知識及核心原理詳解。前後端分離和不分離的優勢與弊端

無敵UFO發表於2019-04-10

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完成即可,頁面中需要動態呈現內容的部分,都是交給後臺開發工程師做資料繫結和基於伺服器進行渲染的(伺服器端渲染)

非完全前後端分離:

非完全前後端分離

【優勢】

  1. 動態展示的資料在頁面的原始碼中可以看見,有利於SEO優化推廣(有利於搜尋引擎的收錄和抓取)
  2. 從伺服器端獲取的結果已經是解析渲染完成的了,不需要客戶端再去解析渲染了,所以頁面載入速度快(前提是伺服器端處理的速度夠快,能夠處理過來),所以類似於京東,淘寶這些網站,首屏資料一般都是由伺服器端渲染的

【弊端】

  1. 如果頁面中存在實時更新的資料,每一次想要展示最新的資料,頁面都要重新重新整理一次,這樣肯定不行,非常耗效能
  2. 都交給伺服器端做資料渲染,伺服器端的壓力太大,如果伺服器處理不過來,頁面呈現的速度更慢(所以像京東和淘寶這類的網站,除了首屏是伺服器端渲染的,其他屏一般都是客戶端做資料渲染繫結的)
  3. 這種模式不利於開發,(開發效率低)

區域性重新整理

目前市場上大部分專案都是前後端完全分離的專案(也有非完全前後端分離的佔少數)

前後端完全分離的專案,頁面中需要動態繫結的資料是交給客戶端完成渲染的

  1. 向伺服器端傳送AJAX請求
  2. 把從伺服器端獲取的資料解析處理,拼接成我們需要展示的HTML字串
  3. 把拼接好的字串替換頁面中的某一部分內容(區域性重新整理),頁面不需要整體重新載入,區域性渲染即可

前後端完全分離:

前後端完全分離

【優勢】

  1. 我們可以根據需求,任意修改頁面中的某一部分內容(例如實時重新整理),整體頁面不重新整理,效能好,體驗好(所有表單驗證,需要實時重新整理的等需求都要基於AJAX實現)

  2. 有利於開發,提高開發效率

    1)前後端的完全分離,後臺不需要考慮前端如何實現,前端也不需要考慮後臺用什麼技術,真正意義上實現了技術的劃分

    2)可以同時進行開發:專案開發開始,首先制定前後端資料互動的介面文件(文件中包含了,調取哪個介面或者哪些資料等協議規範),後臺把介面先寫好(目前很多公司也需要前端自己拿NODE來模擬這些介面),客戶端按照介面調取即可,後臺再去實現介面功能即可

【弊端】

  1. 不利於SEO優化:第一次從伺服器端獲取的內容不包含需要動態繫結的資料,所以頁面的原始碼中沒有這些內容,不利於SEO收錄,後期通過JS新增到頁面中的內容,並不會寫在頁面的原始碼中(是原始碼不是頁面結構)
  2. 交由客戶端渲染,首先需要把頁面呈現,然後在通過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請求方式]

  1. GET請求系列(獲取)
    • get
    • delete:從伺服器上刪除某些資原始檔
    • head:只想獲取伺服器返回的響應頭資訊(響應主體不需要獲取)
  2. POST請求系列(推送)
    • post
    • put:向伺服器中增加指定的資原始檔

不管哪一種請求方式,客戶端都可以把資訊傳遞給伺服器端,伺服器端也可以把資訊返回給客戶端,只是GET系列一般以獲取為主(給的少,拿回來的多),而POST系列一般以推送為主(給的多,拿回來的少)

1)我們想要獲取一些動態展示的資訊,一般使用GET請求,因為只需要向伺服器端傳送請求,告訴伺服器端我們想要什麼,伺服器端就會把需要的資料返回

2)在實現註冊功能的時候,我們需要把客戶輸入的資訊傳送給伺服器進行儲存,伺服器一般返回成功或失敗等狀態,此時我們一般都是基於POST請求完成的


GET系列請求和POST系列請求,在專案實戰中存在很多的區別

  1. GET請求傳遞給伺服器的內容一般沒有POST請求傳遞給伺服器的內容多

    原因:GET請求傳遞給伺服器內容一般都是基於 URL地址問號傳遞引數 來實現的,而POST請求一般都是基於 設定請求主體 來實現的,各個瀏覽器都有URL最大長度的限制(谷歌:8kb,火狐:7kb,IE:2kb),超出長度部分,瀏覽器會自動擷取掉,導致傳遞給伺服器的資料缺失

    理論上POST請求通過請求主體傳遞是沒有大小限制的,真實專案中為了保證傳輸的速率,我們也會限制大小(例如:上傳的資料或者圖片我們會做大小的限制)

  2. 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)

複製程式碼
  1. 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中常用的編碼和解碼方法

正常的編碼解碼(非加密)

  1. escape / unescape: 主要就是把中文漢字進行編碼和解碼(一般只有JS語言支援:也經常應用於前端頁面通訊時候的中文漢字編碼)
str = '你好海洋 哈哈'
"你好海洋 哈哈"
escape(str)
"%u4F60%u597D%u6D77%u6D0B%20%u54C8%u54C8"
unescape("%u4F60%u597D%u6D77%u6D0B%20%u54C8%u54C8")
"你好海洋 哈哈"
複製程式碼
  1. 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")
"你好海洋 哈哈"
複製程式碼
  1. 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;
}
複製程式碼

也可以通過加密的方法進行編碼解碼

  1. 可逆轉加密(一般都是團隊自己用的規則)

  2. 不可逆轉加密(一般都是基於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
複製程式碼

ajax


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
複製程式碼

ajax2


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 這樣的字串傳遞給伺服器
    asynctrue//設定同步或者非同步,預設是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({});

複製程式碼

相關文章