调用jQuery
的情况下,我们通常用来请求数据的方法有
$(element).load(url, callback)
$.get(url, data, callback, type)
$.post(url, data, callback, type)
$.getJSON(url, data, callback)
$.getScript(url, callback)
$.ajax(options)
前五种方法,在jQuery
的实现中,本质上还是在调用第六种方法实现的
$().load() -> jQuery.fn.load -> $.ajax()$.get() -> jQuery.each( [ "get", "post" ], function() {}) -> $.ajax()$.post() -> jQuery.each( [ "get", "post" ], function() {}) -> $.ajax()$.getJSON -> getJSON() -> $.get() -> $.ajax()$.getScript -> getScript() -> $.get() -> $.ajax()
单纯在源码中看前五个函数,代码量都很少,多一点也就是$().load()
函数,涉及到了deferred
的写法,在调用成功时,对返回的数据使用内部方法html()
进行渲染,但是代码也不难看懂
下面重点介绍$().ajax()
方法,ajax
内部结构为
jQuery.extend({ active // 调用ajax的次数 lastModified // 缓存标识 etag // 缓存标识 ajaxSettings // 默认参数设置 ajaxSetup // 开发者自定义参数与默认参数复制到当前ajax的参数中 ajaxPrefilter // 触发ajax调用前的预处理函数 ajaxTransport // 触发ajax调用后的处理函数 ajax // ajax的主函数 getJSON // getJSON 函数 getScript // getScript 函数})
重点需要介绍的函数有三个ajaxPrefilter
,ajaxTransport
, ajax
ajaxPrefilter
函数通过addToPrefiltersOrTransports( prefilters )
实现的,看下jQuery
通过ajaxPrefilter
做了哪些操作
-
jQuery.ajaxPrefilter( "script", function( s ) {})
如果dataType
是script
的话,设置缓存操作,如果是跨域的话,type
就必须设置为get
-
jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) {})
如果dataType
为json jsonp
的话,就需要做进一步的操作了// Detect, normalize options and install callbacks for jsonp requestsjQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) {var callbackName, overwritten, responseContainer, // 判断json是在url中还是在form中 jsonProp = s.jsonp !== false && ( rjsonp.test( s.url ) ? "url" : typeof s.data === "string" && !( s.contentType || "" ).indexOf("application/x-www-form-urlencoded") && rjsonp.test( s.data ) && "data" );// Handle iff the expected data type is "jsonp" or we have a parameter to setif ( jsonProp || s.dataTypes[ 0 ] === "jsonp" ) { // Get callback name, remembering preexisting value associated with it // 如果jsonp的callback是个函数的话,就执行完函数后返回结果,如果不是函数,就返回自身 callbackName = s.jsonpCallback = jQuery.isFunction( s.jsonpCallback ) ? s.jsonpCallback() : s.jsonpCallback; // Insert callback into url or form data // 把callback插入到form表单中,如果使用jsonp的话,就把callback放到url的后面 if ( jsonProp ) { s[ jsonProp ] = s[ jsonProp ].replace( rjsonp, "$1" + callbackName ); } else if ( s.jsonp !== false ) { s.url += ( ajax_rquery.test( s.url ) ? "&" : "?" ) + s.jsonp + "=" + callbackName; } // Use data converter to retrieve json after script execution // responseContainer为冲送到url的数据 s.converters["script json"] = function() { if ( !responseContainer ) { jQuery.error( callbackName + " was not called" ); } // 返回 jsonp 的callback的参数 return responseContainer[ 0 ]; }; // force json dataType s.dataTypes[ 0 ] = "json"; // Install callback // responseContainer 拿到 callback 函数中的参数,就是触发 callback 函数的时,传递给 callback 的参数 // ajax 触发成功后,success中的第一个参数就是 传递给 callback 的参数 overwritten = window[ callbackName ]; window[ callbackName ] = function() { responseContainer = arguments; }; // Clean-up function (fires after converters) jqXHR.always(function() { // Restore preexisting value window[ callbackName ] = overwritten; // Save back as free if ( s[ callbackName ] ) { // make sure that re-using the options doesn't screw things around s.jsonpCallback = originalSettings.jsonpCallback; // save the callback name for future use oldCallbacks.push( callbackName ); } // Call if it was a function and we have a response // 触发 callback 函数 if ( responseContainer && jQuery.isFunction( overwritten ) ) { overwritten( responseContainer[ 0 ] ); } // 清空 callback 函数 responseContainer = overwritten = undefined; }); // Delegate to script // 注意,这里返回script,表明会使用script的方法处理 return "script";}});
然后再看addToPrefiltersOrTransports()
函数,会根据每种dataType
存储对应的函数
// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport// 添加 prefilters(预处理器)/transports(分发处理器) 里面的函数function addToPrefiltersOrTransports( structure ) { // dataTypeExpression is optional and defaults to "*" return function( dataTypeExpression, func ) { // 这是在操作原生的 Ajax 的时候,没有传入 dataTypeExpression if ( typeof dataTypeExpression !== "string" ) { func = dataTypeExpression; dataTypeExpression = "*"; } var dataType, i = 0, dataTypes = dataTypeExpression.toLowerCase().match( core_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 ); } } } };}
然后我们再看ajaxTransport
函数,该函数也是通过addToPrefiltersOrTransports()
函数来存储某种dataType
类型对应的函数
// Bind script tag hack transport// 如果有跨域的话,就会有返回值,没有跨域的话,就没有返回值// 这时候到 inpectPrefiltersOrTransports 函数中看 return !(selected = dataTypeOrTransport) 中的 dataTypeOrTransport 是有值的jQuery.ajaxTransport( "script", function( s ) { // This transport only deals with cross domain requests // 如果需要跨域的情况下 if ( s.crossDomain ) { var script, callback; return { // send函数为在请求的url中创建script标签 send: function( _, complete ) { // 跨域的操作,就是动态创建script script = jQuery("
总共有两处使用ajaxTransport
函数,第二处没有dataType
, 我们在addPrefiltersOrTransports()
中能看到,如果只传递一个参数,那么dataType
就为*
,这时候处理的就是,调用原生ajax
来发送数据
jQuery.ajaxTransport(function( options ) { var callback; // Cross domain only allowed if supported through XMLHttpRequest // 不跨域的时候,就是调原生的 ajax if ( jQuery.support.cors || xhrSupported && !options.crossDomain ) { return { send: function( headers, complete ) { var i, id, xhr = options.xhr(); xhr.open( options.type, options.url, options.async, options.username, options.password ); // 省略了部分代码 // Callback callback = function( type ) { return function() { // 省略了部分代码 }; // Listen to events xhr.onload = callback(); // 没有采用 onreadystatechange(传统是采用这种方式),所有高级的浏览器都支持 onload // 省略了部分代码 // Do send the request // This may raise an exception which is actually // handled in jQuery.ajax (so no try/catch here) xhr.send( options.hasContent && options.data || null ); }, abort: function() { if ( callback ) { callback(); } } }; }});