原生JavaScript實現AJAX、JSONP

 

原生JavaScript實現AJAX、JSONP
 
相信大多數前端開發者在須要與後端進行數據交互時,爲了方便快捷,都會選擇JQuery中封裝的AJAX方法,可是有些時候,咱們只須要JQueryAJAX請求方法,而其餘的功能用到的不多,這顯然是不必的。
 
其實,原生JavaScript實現AJAX並不難,這篇文章將會講解如何實現簡單的AJAX,還有跨域請求JSONP
 
1、AJAX
 
AJAX的核心是XMLHttpRequest
 
一個完整的AJAX請求通常包括如下步驟:
  • 實例化XMLHttpRequest對象
  • 鏈接服務器
  • 發送請求
  • 接收響應數據
 
我將AJAX請求封裝成ajax()方法,它接受一個配置對象params
 
 
function ajax(params) {   
  params = params || {};   
  params.data = params.data || {};   
  // 判斷是ajax請求仍是jsonp請求
  var json = params.jsonp ? jsonp(params) : json(params);   
  // ajax請求   
  function json(params) {   
    //  請求方式,默認是GET
    params.type = (params.type || 'GET').toUpperCase(); 
    // 避免有特殊字符,必須格式化傳輸數據 
    params.data = formatParams(params.data);   
    var xhr = null;   

    // 實例化XMLHttpRequest對象   
    if(window.XMLHttpRequest) {   
      xhr = new XMLHttpRequest();   
    } else {   
      // IE6及其如下版本   
      xhr = new ActiveXObjcet('Microsoft.XMLHTTP');   
    }; 

    // 監聽事件,只要 readyState 的值變化,就會調用 readystatechange 事件
    xhr.onreadystatechange = function() { 
      //  readyState屬性表示請求/響應過程的當前活動階段,4爲完成,已經接收到所有響應數據
      if(xhr.readyState == 4) {   
        var status = xhr.status; 
        //  status:響應的HTTP狀態碼,以2開頭的都是成功
        if(status >= 200 && status < 300) {   
          var response = ''; 
          // 判斷接受數據的內容類型 
          var type = xhr.getResponseHeader('Content-type');   
          if(type.indexOf('xml') !== -1 && xhr.responseXML) {   
            response = xhr.responseXML; //Document對象響應   
          } else if(type === 'application/json') {   
            response = JSON.parse(xhr.responseText); //JSON響應   
          } else {   
            response = xhr.responseText; //字符串響應   
          }; 
          // 成功回調函數 
          params.success && params.success(response);   
       } else {   
          params.error && params.error(status);   
       }   
      };   
    }; 
 
    // 鏈接和傳輸數據   
    if(params.type == 'GET') {
      // 三個參數:請求方式、請求地址(get方式時,傳輸數據是加在地址後的)、是否異步請求(同步請求的狀況極少);
      xhr.open(params.type, params.url + '?' + params.data, true);   
      xhr.send(null);   
    } else {   
      xhr.open(params.type, params.url, true);   
      //必須,設置提交時的內容類型   
      xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8'); 
      // 傳輸數據 
      xhr.send(params.data);   
    }   
  } 
 
  //格式化參數   
  function formatParams(data) {   
    var arr = [];   
    for(var name in data) { 
      //   encodeURIComponent() :用於對 URI 中的某一部分進行編碼
      arr.push(encodeURIComponent(name) + '=' + encodeURIComponent(data[name]));   
    };   
    // 添加一個隨機數參數,防止緩存   
    arr.push('v=' + random());   
    return arr.join('&');   
  }

  // 獲取隨機數   
  function random() {   
    return Math.floor(Math.random() * 10000 + 500);   
  }
}

 

在上面的代碼中readyState的值表示請求/響應過程的當前活動階段,其值得含義以下:
  • 0:爲初始化。還沒有調用open()方法。
  • 1:啓動。已經調用open方法,但還沒有調用send()方法。
  • 2:發送。已經調用send()方法,但還沒有接受到響應。
  • 3:接收。已經接收到部分響應數據。
  • 4:完成。已經接收到所有響應數據,而且能夠在客戶端使用
使用實例:
 
ajax({   
  url: 'test.php',   // 請求地址
  type: 'POST',   // 請求類型,默認"GET",還能夠是"POST"
  data: {'b': '異步請求'},   // 傳輸數據
  success: function(res){   // 請求成功的回調函數
    console.log(JSON.parse(res));   
  },
  error: function(error) {}   // 請求失敗的回調函數
});

 

2、JSONP
 
同源策略
 
AJAX之因此須要「跨域」,罪魁禍首就是瀏覽器的同源策略。即,一個頁面的AJAX只能獲取這個頁面相同源或者相同域的數據。 如何叫「同源」或者「同域」呢?——協議、域名、端口號都必須相同。例如
 
   
http://example.com 和 https://example.com 不一樣,由於協議不一樣;   
http://localhost:8080 和 http://localhost:1000 不一樣,由於端口不一樣;   
http://localhost:8080 和 https://example.com 不一樣,協議、域名、端口號都不一樣,根本不是一家的。
 
  
當跨域請求時,通常都會看到這個錯誤:
XMLHttpRequest cannot load http://ghmagical.com/article/?intro=jsonp%E8%AF%B7%E6%B1%82&v=5520. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost' is therefore not allowed access

 

那如何跨域請求呢?這時,JSONP就登場了!
 
JSONP是JSON with Padding(填充式JSON或參數式JSON)的簡寫,是一種很是經常使用的跨域請求方式。主要原理是利用了script 標籤能夠跨域請求的特性,由其 src 屬性發送請求到服務器,服務器返回 JavaScript 代碼,瀏覽器接受響應,而後就直接執行了,這和經過 script 標籤引用外部文件的原理是同樣的。
 
JSONP由兩部分組成:回調函數數據,回調函數是當響應到來時應該在頁面中調用的函數,回調函數的名字通常在請求中指定。當服務器響應時,服務器端就會把該函數和數據拼成字符串返回。
JSONP的請求過程:
  • 請求階段:瀏覽器建立一個 script 標籤,並給其src 賦值(相似 http://example.com/api/?callback=jsonpCallback)。
  • 發送請求:當給scriptsrc賦值時,瀏覽器就會發起一個請求。
  • 數據響應:服務端將要返回的數據做爲參數和函數名稱拼接在一塊兒(格式相似」jsonpCallback({name: 'abc'})」)返回。當瀏覽器接收到了響應數據,因爲發起請求的是 script,因此至關於直接調用 jsonpCallback 方法,而且傳入了一個參數。
 
在這裏講解一下用原生JavaScript如何實現。
 
依舊是ajax()方法裏添加JSONP,後面會將二者整合在一塊兒,JSONP的配置參數主要多了一個jsonp參數,它就是你的回調函數名。
function ajax(params) {   
  params = params || {};   
  params.data = params.data || {};   
  var json = params.jsonp ? jsonp(params) : json(params);      
  // jsonp請求   
  function jsonp(params) {   
    //建立script標籤並加入到頁面中   
    var callbackName = params.jsonp;   
    var head = document.getElementsByTagName('head')[0];   
    // 設置傳遞給後臺的回調參數名   
    params.data['callback'] = callbackName;   
    var data = formatParams(params.data);   
    var script = document.createElement('script');   
    head.appendChild(script);    
    //建立jsonp回調函數   
    window[callbackName] = function(json) {   
      head.removeChild(script);   
      clearTimeout(script.timer);   
      window[callbackName] = null;   
      params.success && params.success(json);   
    };    
    //發送請求   
    script.src = params.url + '?' + data;    
    //爲了得知這次請求是否成功,設置超時處理   
    if(params.time) {   
     script.timer = setTimeout(function() {   
       window[callbackName] = null;   
       head.removeChild(script);   
       params.error && params.error({   
         message: '超時'   
       });   
     }, time);   
    }   
  };    
  //格式化參數   
  function formatParams(data) {   
    var arr = [];   
    for(var name in data) {   
      arr.push(encodeURIComponent(name) + '=' + encodeURIComponent(data[name]));   
    };   

    // 添加一個隨機數,防止緩存   
    arr.push('v=' + random());   
    return arr.join('&');   
  }   

  // 獲取隨機數   
  function random() {   
    return Math.floor(Math.random() * 10000 + 500);   
  } }

 


 
  
注意:由於 script 標籤的 src 屬性只在第一次設置的時候起做用,致使 script 標籤無法重用,因此每次完成操做以後要移除;
使用實例:
 
ajax({   
   url: 'test',    // 請求地址
  jsonp: 'jsonpCallback',  // 採用jsonp請求,且回調函數名爲"jsonpCallbak",能夠設置爲合法的字符串
  data: {'b': '異步請求'},   // 傳輸數據
  success:function(res){   // 請求成功的回調函數
    console.log(res);   
  },
  error: function(error) {}   // 請求失敗的回調函數
});
 
雖然JSONP很是簡單易用,不過,它也存在兩點不足
  •  JSONP是從其餘域加載代碼執行,若是其餘域不安全,可能會夾帶一些惡意代碼,而此時除了徹底放棄JSONP調用以外,沒辦法追究
  • 要確認JSONP請求是否失敗並不容易
相關文章
相關標籤/搜索