JQuery3.1.1源碼解讀(十九)【ajax】

來源:互聯網
上載者:User

關於 ajax,東西太多了,我本來想避開 ajax,避而不提,但覺得 ajax 這麼多內容,不說實在可惜。 寫在 jQuery 的 ajax 之前

首先,我們還是來瞭解一下 js 中的 http 請求。http 協議中有請求體和響應體,對於請求的一方,無論是哪一種語言,我比較關心如下幾個方面:請求的配置參數包括 url,post/get 等;請求有要求標頭,那麼要求標頭的參數又該由哪個函數來設定;如何判斷請求已經成功;響應狀態代碼和響應資料該如何獲得等等。 XMLHttpRequest 對象

每天都喊著要寫原生的 js 請求,那麼來了,就是這個函數 XMLHttpRequest,它是一套可以在Javascript、VbScript、Jscript等指令碼語言中通過http協議傳送或接收XML及其他資料的一套API,萬惡的低版本 IE 有個相容的 ActiveXObject。

它有兩個版本,第一個版本的功能很少,在不久之後又有了點一個更完善的版本 2.0,功能更全。如果你感興趣,可以來這裡看一下XMLHttpRequest。如果你對 http 協議有著很好的掌握的話,也可以看下面的內容。 實現一個簡單的 ajax 請求

如果你碰到面試官,讓你手寫一個原生的 ajax 請求,那麼下面的東西可能對你非常有協助:

// myAjaxvar myAjax = (function(){  var defaultOption = {    url: false,    type: 'GET',    data: null,    success: false,    complete: false  }  var ajax = function(options){    for(var i in defaultOption){      options[i] = options[i] || defaultOption[i];    }    // http 對象    var xhr = new XMLHttpRequest();    var url = options.url;    xhr.open(options.type, url);    // 監聽    xhr.onreadystatechange = function(){      if(xhr.readyState == 4){        var result, status = xhr.status;      }      if(status >= 200 && status < 300 || status == 304){        result = xhr.responseText;        if(window.JSON){          result = JSON.parse(result);        }else{          result = eval('(' + result + ')');        }        ajaxSuccess(result)      }    }    // post    if(options.type.toLowerCase() === 'post'){      xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencode');    }    xhr.send(options.data);    function ajaxSuccess(data){      var status = 'success';      options.success && options.success(data, options, status, xhr);      options.complete && options.complete(status);    }  }  // 閉包返回  return ajax;})()

測試在下面:

var success = function(data){  console.log(data['blog'])}var complete = function(status){  if(status == 'success'){    console.log('success')  }else{    console.log('failed')  }}myAjax( {  url: 'https://api.github.com/users/songjinzhong',  success: success,  complete: complete} );

可以得到 XMLHttpRequest 的簡單用法: 通過 new XMLHttpRequest() 建立一個 http 請求對象; open 函數的作用是設定要開啟的 url 和類型,建立一個串連,但此時請求並沒有發送; setRequestHeader 來佈建要求頭資訊; send 函數像伺服器發送資料請求; onreadystatechange 是一個監聽函數,當 readyState 改變的時候執行,1-2-3-4,4 表示成功返回。xhr.responseText 是返回的響應資料,很明顯,這裡是 json 格式,實際要通過回應標頭來判斷,這裡省去了這一步,getAllResponseHeaders 可以獲得所有回應標頭; success 函數和 complete 函數執行的位置和順序問題。 jQuery ajax 的特點

通過上面的例子,應該可以對 js 的 http 請求有個大致的瞭解,而 jQuery 的處理則複雜的多,也涉及到和上面功能類似的一些函數,而對於 callback 和 deferred,jQuery 本身就支援:

var deferred = jQuery.Deferred(),  completeDeferred = jQuery.Callbacks( "once memory" );

所以說 jQuery 是一個自給自足的庫,一點也不過分,前面有 Sizzle,整個源碼到處都充滿著 extend 函數,等等。 jQuery.ajaxSetup

ajaxSetup 是在 ajax 函數裡比較早執行的一個函數,這個函數主要是用來校準參數用的;

jQuery.extend( {  ajaxSetup: function( target, settings ) {    return settings ?      // 雙層的 ajaxExtend 函數      ajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) :      // Extending ajaxSettings      ajaxExtend( jQuery.ajaxSettings, target );  },} );

ajaxSettings 是一個對象,具體是幹什麼用的,看看就知道了:

jQuery.ajaxSettings = {  url: location.href,  type: "GET",  isLocal: rlocalProtocol.test( location.protocol ),  global: true,  processData: true,  async: true,  contentType: "application/x-www-form-urlencoded; charset=UTF-8",  accepts: {    "*": allTypes,    text: "text/plain",    html: "text/html",    xml: "application/xml, text/xml",    json: "application/json, text/javascript"  },  contents: {    xml: /\bxml\b/,    html: /\bhtml/,    json: /\bjson\b/  },  responseFields: {    xml: "responseXML",    text: "responseText",    json: "responseJSON"  },  // Data converters  // Keys separate source (or catchall "*") and destination types with a single space  converters: {    // Convert anything to text    "* text": String,    // Text to html (true = no transformation)    "text html": true,    // Evaluate text as a json expression    "text json": JSON.parse,    // Parse text as xml    "text xml": jQuery.parseXML  },  // For options that shouldn't be deep extended:  // you can add your own custom options here if  // and when you create one that shouldn't be  // deep extended (see ajaxExtend)  flatOptions: {    url: true,    context: true  }}

ajaxSettings 原來是一個加強版的 options。

ajaxExtend 是用來將 ajax 函數參數進行標準化的,看看哪些參數沒有賦值,讓它等於預設值,由於 ajaxExtend 是雙層的,具體要調試了才能更明白。

function ajaxExtend( target, src ) {  var key, deep,    flatOptions = jQuery.ajaxSettings.flatOptions || {};  for ( key in src ) {    if ( src[ key ] !== undefined ) {      ( flatOptions[ key ] ? target : ( deep || ( deep = {} ) ) )[ key ] = src[ key ];    }  }  if ( deep ) {    jQuery.extend( true, target, deep );  }  return target;}
ajax.jqXHR

在 ajax 中有一個非常重要的對象,jqXHR,它雖然是一個簡稱,但通過縮寫也大致能猜出它是 jquery-XMLHttpRequest。

jqXHR = {  readyState: 0, // 0-4  // 熟悉回應標頭的對這個應該不陌生,將回應標頭資料按照 key value 儲存起來  getResponseHeader: function( key ) {    var match;    if ( completed ) {      if ( !responseHeaders ) {        responseHeaders = {};        while ( ( match = /^(.*?):[ \t]*([^\r\n]*)$/mg.exec( responseHeadersString ) ) ) {          responseHeaders[ match[ 1 ].toLowerCase() ] = match[ 2 ];        }      }      match = responseHeaders[ key.toLowerCase() ];    }    return match == null ? null : match;  },  // Raw string  getAllResponseHeaders: function() {    return completed ? responseHeadersString : null;  },  // 手動佈建要求頭  setRequestHeader: function( name, value ) {    if ( completed == null ) {      name = requestHeadersNames[ name.toLowerCase() ] =        requestHeadersNames[ name.toLowerCase() ] || name;      requestHeaders[ name ] = value;    }    return this;  },  // Overrides response content-type header  overrideMimeType: function( type ) {    if ( completed == null ) {      s.mimeType = type;    }    return this;  },  // Status-dependent callbacks  statusCode: function( map ) {    var code;    if ( map ) {      if ( completed ) {        // Execute the appropriate callbacks        jqXHR.always( map[ jqXHR.status ] );      } else {        // Lazy-add the new callbacks in a way that preserves old ones        for ( code in map ) {          statusCode[ code ] = [ statusCode[ code ], map[ code ] ];        }      }    }    return this;  },  // Cancel the request  abort: function( statusText ) {    var finalText = statusText || strAbort;    if ( transport ) {      transport.abort( finalText );    }    done( 0, finalText );    return this;  }};

jqXHR 已經完全可以取代 XHR 對象了,函數都進行擴充了。 ajaxTransport

那麼 XMLHttpRequest 這個函數到底在哪呢。

jQuery 中有兩個屬性,分別是 ajaxPrefilter 和 ajaxTransport,它們是由 addToPrefiltersOrTransports 函數構造的。主要來看 ajaxTransport 函數:

jQuery.ajaxTransport( function( options ) {  var callback, errorCallback;  // Cross domain only allowed if supported through XMLHttpRequest  if ( support.cors || xhrSupported && !options.crossDomain ) {    return {      send: function( headers, complete ) {        var i,          xhr = options.xhr();// xhr() = XMLHttpRequest()        xhr.open(          options.type,          options.url,          options.async,          options.username,          options.password        );        // Apply custom fields if provided        if ( options.xhrFields ) {          for ( i in options.xhrFields ) {            xhr[ i ] = options.xhrFields[ i ];          }        }        // Override mime type if needed        if ( options.mimeType && xhr.overrideMimeType ) {          xhr.overrideMimeType( options.mimeType );        }        // X-Requested-With header        // For cross-domain requests, seeing as conditions for a preflight are        // akin to a jigsaw puzzle, we simply never set it to be sure.        // (it can always be set on a per-request basis or even using ajaxSetup)        // For same-domain requests, won't change header if already provided.        if ( !options.crossDomain && !headers[ "X-Requested-With" ] ) {          headers[ "X-Requested-With" ] = "XMLHttpRequest";        }        // Set headers        for ( i in headers ) {          xhr.setRequestHeader( i, headers[ i ] );        }        // Callback        callback = function( type ) {          return function() {            if ( callback ) {              callback = errorCallback = xhr.onload =                xhr.onerror = xhr.onabort = xhr.onreadystatechange = null;              if ( type === "abort" ) {                xhr.abort();              } else if ( type === "error" ) {                // Support: IE <=9 only                // On a manual native abort, IE9 throws                // errors on any property access that is not readyState                if ( typeof xhr.status !== "number" ) {                  complete( 0, "error" );                } else {                  complete(                    // File: protocol always yields status 0; see #8605, #14207                    xhr.status,                    xhr.statusText                  );                }              } else {                complete(                  xhrSuccessStatus[ xhr.status ] || xhr.status,                  xhr.statusText,                  // Support: IE <=9 only                  // IE9 has no XHR2 but throws on binary (trac-11426)                  // For XHR2 non-text, let the caller handle it (gh-2498)                  ( xhr.responseType || "text" ) !== "text"  ||                  typeof xhr.responseText !== "string" ?                    { binary: xhr.response } :                    { text: xhr.responseText },                  xhr.getAllResponseHeaders()                );              }            }          };        };        // Listen to events        xhr.onload = callback();        errorCallback = xhr.onerror = callback( "error" );        // Support: IE 9 only        // Use onreadystatechange to replace onabort        // to handle uncaught aborts        if ( xhr.onabort !== undefined ) {          xhr.onabort = errorCallback;        } else {          xhr.onreadystatechange = function() {            // Check readyState before timeout as it changes            if ( xhr.readyState === 4 ) {              // Allow onerror to be called first,              // but that will not handle a native abort              // Also, save errorCallback to a variable              // as xhr.onerror cannot be accessed              window.setTimeout( function() {                if ( callback ) {                  errorCallback();                }              } );            }          };        }        // Create the abort callback        callback = callback( "abort" );        try {          // Do send the request (this may raise an exception)          xhr.send( options.hasContent && options.data || null );        } catch ( e ) {          // #14683: Only rethrow if this hasn't been notified as an error yet          if ( callback ) {            throw e;          }        }      },      abort: function() {        if ( callback ) {          callback();        }      }    };  }} );

ajaxTransport 函數傳回值有兩個,其中 send 就是發送函數了,一步一步,發送下來,無需多說明。

另外,ajax 對於 jQuery 對象在 ajax 過程提供了很多回呼函數:

jQuery.each( [  "ajaxStart",  "ajaxStop",  "ajaxComplete",  "ajaxError",  "ajaxSuccess",  "ajaxSend"], function( i, type ) {  jQuery.fn[ type ] = function( fn ) {    return this.on( type, fn );  };} );jQuery.event.trigger( "ajaxStart" );...globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] );...globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError",[ jqXHR, s, isSuccess ? success : error ] );...globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] );...jQuery.event.trigger( "ajaxStop" );

ajax 東西太多了,至少有 1000 行的代碼吧。 總結

關於 ajax,不想去深入研究了,最近暑假實習校招已經開始啟動了,暫時先放一放吧,以後有時間再來填坑吧。 參考

jQuery源碼分析系列(30) : Ajax 整體結構

jQuery源碼分析系列(37) : Ajax 總結

觸碰jQuery:AJAX非同步詳解

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.