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

AJAX基礎知識及核心原理解讀

AJAX基礎知識

什麼是AJAX?

async javascript and xml 異步的JS和XMLjavascript

XML:可擴展的標記語言

做用:是用來存儲數據的(經過本身擴展的標記名稱清晰地展現出數據結構)

ajax之因此稱爲異步的js和xml,主要緣由是:之前最開始使用ajax實現客戶端和服務端數據通訊的時候,傳輸數據的格式通常都是xml格式的數據,咱們把他稱之爲異步的js和xml(如今通常都是基於JSON格式來進行數據傳輸的)html

<?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完成便可,頁面中須要動態呈現內容的部分,都是交給後臺開發工程師作數據綁定和基於服務器進行渲染的(服務器端渲染)前端

圖片描述

【優點】java

  1. 動態展現的數據在頁面的源代碼中能夠看見,有利於SEO優化推廣(有利於搜索引擎的收錄和抓取)
  2. 從服務器端獲取的結果已是解析渲染完成的了,不須要客戶端再去解析渲染了,因此頁面加載速度快(前提是服務器端處理的速度夠快,可以處理過來),因此相似於京東,淘寶這些網站,首屏數據通常都是由服務器端渲染的

【弊端】git

  1. 若是頁面中存在實時更新的數據,每一次想要展現最新的數據,頁面都要從新刷新一次,這樣確定不行,很是耗性能
  2. 都交給服務器端作數據渲染,服務器端的壓力太大,若是服務器處理不過來,頁面呈現的速度更慢(因此像京東和淘寶這類的網站,除了首屏是服務器端渲染的,其餘屏通常都是客戶端作數據渲染綁定的)
  3. 這種模式不利於開發,(開發效率低)

局部刷新github

目前市場上大部分項目都是先後端徹底分離的項目(也有非徹底先後端分離的佔少數)面試

先後端徹底分離的項目,頁面中須要動態綁定的數據是交給客戶端完成渲染的ajax

  1. 向服務器端發送AJAX請求
  2. 把從服務器端獲取的數據解析處理,拼接成咱們須要展現的HTML字符串
  3. 把拼接好的字符串替換頁面中的某一部份內容(局部刷新),頁面不須要總體從新加載,局部渲染便可

圖片描述

【優點】編程

  1. 咱們能夠根據需求,任意修改頁面中的某一部份內容(例如實時刷新),總體頁面不刷新,性能好,體驗好(全部表單驗證,須要實時刷新的等需求都要基於AJAX實現)
  2. 有利於開發,提升開發效率

    1)先後端的徹底分離,後臺不須要考慮前端如何實現,前端也不須要考慮後臺用什麼技術,真正意義上實現了技術的劃分json

    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

圖片描述


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({});
相關文章
相關標籤/搜索