ajax 原始碼解讀之如何擴充套件 ajax 的功能

wangfulin發表於2016-04-06

需求場景:藉助 ajaxTransport 來自定義擴充套件功能。

  1. 專案需求:jQuery 版本: 1.10.2,錯誤打點,發起 jsonp 請求發生錯誤的時候前端需要向打點地址傳送一個請求。

  2. 簡單演示:
    測試程式碼:

       $.ajax(`http://google.com/a.js`, {
           type: `GET`,
           dataType: `jsonp`,
           success: function(success, statusText, jqXHR){
               console.log(`jsonp request success`);
           },
           error: function(jqXHR, statusText, error){
               console.log(`jsonp error`);
           }
       });
       
       由於 1.10.2 版本的 jquery 並沒有對建立的 script 監聽錯誤事件,故無法呼叫 error 方法,故無法進一步將前端資訊錯誤上報
       

    解決方案:

       $.ajaxTransport(`+script`, function(s){
           // This transport only deals with cross domain requests
           var script,
                head = document.head || $(`head`)[0] || document.documentElement;
                
           return {
               send: function(_, callback) {
                   
                   script = document.createElement(`script`);
                   
                   script.async = true;
                   
                   if(s.scriptCharset){
                       script.charset = s.scriptCharset;
                   }
                   
                   script.src = s.url;
                   
                   // Handle error
                   script.onerror = function(err){
                       // Handle memory lead in IE
                       script.onload = script.onreadystatechange = null;
                       script.onerror = null;
                       
                       // Remove the script
                       if(script.parentNode){
                           script.parentnode.removeChild(script);
                       }
                       
                       // Dereference the script
                       script = null;
                       
                       if(err.type === `error`){
                           callback(404, err.type);
                       }
                   }
                   
                   // Attach handlers for all browsers
                   script.onload = script.onreadystatechange = function(_, isAbort){
                       
                       if(isAbort || !script.readyState || 
                       /loaded|complete/.test(script.readyState)){
                           // Handle memeory leak in IE
                           script.onload = script.onreadystatechange = null;
                           
                           // Remove the script
                           if(script.parentNode){
                               script.parentNode.removeChild(script);
                           }
                           
                           // Dereference the script
                           script = null;
                           
                           // Callback if not abort
                           if(!isAbort){
                               callback(200, `success`);
                           }
                       }
                   };
                   
                   // Cicumvent IE6 bugs with base elements (#2709 and #4378) by prepending
                   // Use native DOM manipulation to avoid our domManip AJAX trickery
                   head.insertBefore(script, head.firstChild);
               },
               
               abort: function(){
                   if(script){
                       script.onload(undefined, true);
                   }
               }
           };
       });
    

需求場景:藉助 ajaxSetup 新增 dataType.

  1. 專案需求:需要從伺服器獲取 yaml 檔案,然後解析該檔案

  2. 簡單演示:
    解決方案:

       function parseYaml(text){
           console.log(`You are parsing yaml file!`);
           return `yaml` + text + `yaml`;
       }
       $.ajaxSetup({
           accepts: {
               yaml: `application/x-yaml, text/yaml`
           },
           contents: {
               yaml: /yaml/
           },
           converters: {
               `text yaml`: function(text){
                   return parseYaml(text);
               }
           }
       });

    測試程式碼:

       // 傳送 dataType 為 yaml 的請求
       $.ajax({
           url: `http://google.com/helloworld.yaml`,
           dataType: `yaml`,
           success: function(data){
               console.log(data);
           }
       });
    

需求場景:藉助 ajaxPrefilter 來自定義擴充套件功能。

  1. 專案需求:防止 ajax 請求的重複提交

  2. 簡單演示:
    解決方案:
    var pendingRequests = {};

    function storePendingRequest(key, jqXHR){

       pendingRequests[key] = true;
       jqXHR.pendingRequestKey = key;

    }

    function generatePendingRequestKey(options){

       return (options.type + options.url + options.dataType).toLowerCase().replace(/[^a-z0-9]/g, ``);

    }
    $.ajaxPrefilter(function( options, originalOptions, jqXHR ) {

       
       // 不重複傳送相同請求
       var key = generatePendingRequestKey(options);
       if (!pendingRequests[key]) {
           storePendingRequest(key, jqXHR);
       } else {
           // or do other
           jqXHR.abort();
       }
       
       var complete = options.complete;
       options.complete = function(jqXHR, textStatus) {
           // clear from pending requests
           pendingRequests[jqXHR.pendingRequestKey] = null;
           
           if ($.isFunction(complete)) {
               complete.apply(this, arguments);
           }
       };

    });

    測試程式碼:
    for(var i = 0; i < 10; i++){

       var j = 0;
       $.ajax({
           url: `http://js.passport.qihucdn.com/5.0.2.js`,
           type: `GET`,
           dataType: `HTML`,
           complete: function(){
               console.log(`complete:` + j++);
           }
       });

    }

相關文章