jquery 实现原理五:ajax

来源:互联网 发布:轮滑的好处和坏处 知乎 编辑:程序博客网 时间:2024/05/23 01:58
ajax模块的结构:

一, ajax 实现包括两个部分

1 基本模块ajax,一个ajax.js定义了异步请求的基本结构,主要提供了两个重要的配置
默认配置 ajaxSettings,并可拓展自定义的配置
构造 ajaxPrefilters 和 ajaxTransport,这两个都是key-value对象,其中key是dataType,value是对应的处理函数。
     ajaxPrefilters 是一个过滤器,作用是在发送ajax请求之前做一定的预处理工作,比如:处理参数,注册回调等。
     ajaxTransport是一个分发器,是发送请求的具体实现,比如xhr就用xmlhttprequest来实现,script就通过在head中插入script标签来实现。
2,ajax插件
通过自定义这两个配置,可以以插件形式实现自己的ajax函数。实际上,ajax.js并没有提供任何具体实现,而ajax/目录下通过插件形式实现了四种方法:jsonp, load, script, xhr。
比如script插件,定义了自己的settings,定义了自己的ajaxPrefilter,以及ajaxTransport,主要的实现就是在ajaxTransport函数中在head中插入script标签。

二,ajax的实现

下面我们主要探讨ajax.js的实现。
实现一个ajax最主要的是什么?
显然是对不同dataType的具体处理和实现,比如'jsonp'应该怎么处理,'script'应该怎么处理,'*'应该怎么处理?
对此,ajax模块的做法是,提供一个基本模块ajax,然后通过插件形式来实现对各种不同dataType的具体处理。

下面逐段来分析ajax.js中的重要代码

prefilters = {},
transports = {},
这两行代码定义了两个非常重要的对象,prefilters就是预处理器,在ajax请求发出之前做预处理工作,transports是分发器,负责实际发送ajax请求。
这两个对象的结构是完全一样的,就是key-value类型,其中key表示datatype,value表示对应的处理函数。比如prefilter可能是这样的
{'jsonp': function() {}, '*': function(){}, 'script':function(){}}
ajax会在每个请求发送前,根据datatype调用prefilters中的对应函数进行预处理,然后调用transports中的对应函数来发送请求。
所以,如果没有ajax目录下的插件,ajax.js是没有提供实际的异步请求功能的。

function addToPrefiltersOrTransports( structure ) {

     // dataTypeExpression is optional and defaults to "*"
     return function( dataTypeExpression, func ) {

          if ( typeof dataTypeExpression !== "string" ) {
               func = dataTypeExpression;
               dataTypeExpression = "*";
          }

          var dataType,
               i = 0,
               dataTypes = dataTypeExpression.toLowerCase().match( rnotwhite ) || [];

          if ( jQuery.isFunction( func ) ) {
               // For each dataType in the dataTypeExpression
               while ( (dataType = dataTypes[i++]) ) {
                    // Prepend if requested
                    if ( dataType[0] === "+" ) {
                         dataType = dataType.slice( 1 ) || "*";
                         (structure[ dataType ] = structure[ dataType ] || []).unshift( func );

                    // Otherwise append
                    } else {
                         (structure[ dataType ] = structure[ dataType ] || []).push( func );
                    }
               }
          }
     };
}

这个函数功能非常简单,就是把某一个dataype对应的处理函数func存进structure中,就是一个简单的注册而已。那么structure是什么呢?从后面的代码
ajaxPrefilter: addToPrefiltersOrTransports( prefilters ),
ajaxTransport: addToPrefiltersOrTransports( transports ),
可以得知就是prefilter和transports,因为他们的结构实际上完全一样,所以通过调用同一个函数addToPrefiltersOrTransports来实现的。

// Base inspection function for prefilters and transports
function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) {

     var inspected = {},
          seekingTransport = ( structure === transports );

     function inspect( dataType ) {
          var selected;
          inspected[ dataType ] = true;
          jQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) {
               var dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR );
               if( typeof dataTypeOrTransport === "string" && !seekingTransport && !inspected[ dataTypeOrTransport ] ) {
                    options.dataTypes.unshift( dataTypeOrTransport );
                    inspect( dataTypeOrTransport );
                    return false;
               } else if ( seekingTransport ) {
                    return !( selected = dataTypeOrTransport );
               }
          });
          return selected;
     }

     return inspect( options.dataTypes[ 0 ] ) || !inspected[ "*" ] && inspect( "*" );
}
上面是另一个重要函数,同样很简单,它的功能就是去调用datatype对应的prefilters和transports函数而已。

// Attach a bunch of functions for handling common AJAX events
jQuery.each( [ "ajaxStart", "ajaxStop", "ajaxComplete", "ajaxError", "ajaxSuccess", "ajaxSend" ], function( i, type ){
     jQuery.fn[ type ] = function( fn ){
          return this.on( type, fn );
     };
});

这里注册了一堆快捷方式,所以你用xhr.ajaxStop(fn) 和 xhr.on('ajaxSropt', fun)是完全一样的。

下面就开始调用jquery.extend来在jquery上拓展一些ajax方法了。
     ajaxSettings: {…..}
     默认设置,具体参见源码

ajaxPrefilter: addToPrefiltersOrTransports( prefilters ),
ajaxTransport: addToPrefiltersOrTransports( transports ),
上面提到过的,两个方法暴漏出来是给查件用的,可以注册自己对datatype的处理函数,很简单不多说。

在下面就是ajax函数 函数有点长就不贴了主要做了这些事:
1,创建了一个jqXHR对象,这个对象就是ajax的返回值
2,用deferred对象封装jqXHR对象,因此可以实现链式的异步操作:xhr.complete(x).success(x).errorl(x),这里的complete,success和error就是promise对象的add, done和fail的别名而已。
3,调用函数inspectPrefiltersOrTransports( prefilters, s, options, jqXHR ),那些插件注册的prefilters函数就在这里被调用了。
4,做了一大对后续的处理,比如设置header参数,设置缓存cache
5,调用inspectPrefiltersOrTransports( transports, s, options, jqXHR )函数发送请求
6,定义了done函数,当请求发送结束之后做后续处理,包括调用convert转换结果、设置statusText,调用deferred.resolveWith触发异步回调等
7,最后返回了jqXHR对象

jQuery.each( [ "get", "post" ], function( i, method ) {
     jQuery[ method ] = function( url, data, callback, type ) {
          // shift arguments if data argument was omitted
          if ( jQuery.isFunction( data ) ) {
               type = type || callback;
               callback = data;
               data = undefined;
          }

          return jQuery.ajax({
               url: url,
               type: method,
               dataType: type,
               data: data,
               success: callback
          });
     };
});
上面又是一个快捷方式,get和post就是参数不同的ajax函数而已

function ajaxConvert( s, response, jqXHR, isSuccess ) {}
这个函数就是根据setting中的设置来对请求结果进行类型转换
 
over,以上就是ajax的全部实现

三,插件的实现

有了ajax这个基本模块,那么实现各种插件就非常简单明了,就是要通过ajaxPrefilter和ajaxTransport来注册对应dataType的处理函数就ok了。
比如xhr.js
jQuery.ajaxTransport(function( options ) {
     var callback;
     // Cross domain only allowed if supported through XMLHttpRequest
     if ( jQuery.support.cors || xhrSupported && !options.crossDomain ) {
          return {
               send: function( headers, complete ) {
                   ….
               },
               abort: function() {
                   ...
               }
          };
     }
});
上面就是其主要实现函数的结构,注意这里没有dataType参数,没有dataType就相当于"*",就是对所有请求的默认处理函数,其中的实现就是通过新建XMLHTTPRequest对象来发送请求
     
原创粉丝点击