ajax源码解析-源码流程

ajax源码 了解执行的整个过程 参照注释看相关文章

ajax 源码核心

1.s对象和jqXHR对象
2.jQuery. ajaxPrefilter和jQuery. ajaxTransport
3.跨域请求和xmlHttpRequest请求
4.deferred

参考

ajax源码注解

流程图

源码注解

ajax: function( url, options ) {
//ajax方法参数调整,如果url是object,这是我们一般的调用方式
// If url is an object, simulate pre-1.5 signature
if ( typeof url === "object" ) {
options = url;
url = undefined;
}
//option是一个对象
// Force options to be an object
options = options || {};
var // Cross-domain detection vars
parts,
// Loop variable
i,
// URL without anti-cache param
cacheURL,
// Response headers as string
responseHeadersString,
// timeout handle
timeoutTimer,
// To know if global events are to be dispatched
fireGlobals,
transport,
// Response headers
responseHeaders,
// Create the final options object
// 由ajaxSetting和入口传入的option合成的对象s,
//jQuery.ajaxSetup等函数的使用 参见文章ajax源码解析-jQuery ajax相关函数
//对象s内的各个参数详解,参见文章ajax源码解析-s对象和jqXHR对象
s = jQuery.ajaxSetup( {}, options ),
// Callbacks context
//设置context,如果没有context那么就是返回的最终的options=ajaxSettings+options(用户调用ajax方法时候传送的option)
callbackContext = s.context || s,
//如果传入的对象有context,同时context是DOM对象或者是jQuery对象,那么把该DOM对象封装为jQuery对象
//如果不满足也就是没有context或者context不是DOM对象和jQuery对象,那么globalEventContext就是jQuery.event对象!
// Context for global events is callbackContext if it is a DOM node or jQuery collection
globalEventContext = s.context && ( callbackContext.nodeType || callbackContext.jquery ) ?
jQuery( callbackContext ) :
jQuery.event,
//创建Deferred对象
// Deferreds
deferred = jQuery.Deferred(),
//创建Callbacks对象
completeDeferred = jQuery.Callbacks("once memory"),
//获取最终options的statusCode参数,默认是空对象!
// Status-dependent callbacks
statusCode = s.statusCode || {},
// Headers (they are sent all at once)
requestHeaders = {},
requestHeadersNames = {},
// The jqXHR state
state = 0,
// Default abort message
strAbort = "canceled",
//创建一个伪的xhr对象,该对象有readyState,getResponseHeader,getAllResponseHeaders,setRequestHeader
//overrideMimeType,statusCode,abort方法和属性!
// Fake xhr
jqXHR = {
readyState: 0,
// Builds headers hashtable if needed
getResponseHeader: function( key ) {
var match;
//状态是2的时候才能获取数据
if ( state === 2 ) {
if ( !responseHeaders ) {
responseHeaders = {};
//rheaders = /^(.*?):[ \t]*([^\r\n]*)\r?$/mg, // IE leaves an \r character at EOL
//responseHeaders的键名就是第一个捕获组的数据,第二个键值就是第二个捕获组数据!
while ( (match = rheaders.exec( responseHeadersString )) ) {
responseHeaders[ match[1].toLowerCase() ] = match[ 2 ];
}
}
//返回这个key对应的键值!
match = responseHeaders[ key.toLowerCase() ];
}
return match == null ? null : match;
},
// Raw string
//如果状态是2,那么就是responseHeadersString
getAllResponseHeaders: function() {
return state === 2 ? responseHeadersString : null;
},
// Caches the header
//设置HTTP请求头的时候,头是小写的
setRequestHeader: function( name, value ) {
var lname = name.toLowerCase();
//如果state为0那么就缓存头,把结果放入requestHeaders中!但是要提前查找requestHeadersNames
if ( !state ) {
name = requestHeadersNames[ lname ] = requestHeadersNames[ lname ] || name;
requestHeaders[ name ] = value;
}
return this;
},
// Overrides response content-type header
//如果state=0那么可以覆盖这个mimetype!
overrideMimeType: function( type ) {
if ( !state ) {
s.mimeType = type;
}
return this;
},
// Status-dependent callbacks
statusCode: function( map ) {
var code;
if ( map ) {
if ( state < 2 ) {
for ( code in map ) {
// Lazy-add the new callback in a way that preserves old ones
statusCode[ code ] = [ statusCode[ code ], map[ code ] ];
}
} else {
// Execute the appropriate callbacks
jqXHR.always( map[ jqXHR.status ] );
}
}
return this;
},
// Cancel the request
//取消请求
abort: function( statusText ) {
var finalText = statusText || strAbort;
if ( transport ) {
transport.abort( finalText );
}
done( 0, finalText );
return this;
}
};
function done( status, nativeStatusText, responses, headers ) {
var isSuccess, success, error, response, modified,
statusText = nativeStatusText;
// Called once
//如果state是2,那么直接返回!
if ( state === 2 ) {
return;
}
// State is "done" now
//state设置为2表示不会再次执行了!
state = 2;
// Clear timeout if it exists
//如果timeoutTimer存在,那么直接清除!
if ( timeoutTimer ) {
clearTimeout( timeoutTimer );
}
// Dereference transport for early garbage collection
// (no matter how long the jqXHR object will be used)
transport = undefined;
// Cache response headers
//获取response的头部信息,默认是空!
responseHeadersString = headers || "";
// Set readyState
//如果status>0那么把readyState设置为4!
jqXHR.readyState = status > 0 ? 4 : 0;
// Determine if successful
//如果status在指定的区间内那么表示成功!
isSuccess = status >= 200 && status < 300 || status === 304;
// Get response data
//如果done方法有responses那么对他进行处理!
if ( responses ) {
response = ajaxHandleResponses( s, jqXHR, responses );
}
// Convert no matter what (that way responseXXX fields are always set)
response = ajaxConvert( s, response, jqXHR, isSuccess );
// If successful, handle type chaining
if ( isSuccess ) {
// Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
//如果ifModified存在,那么就要设置If-Modified-Since和If-None-Match头!
if ( s.ifModified ) {
modified = jqXHR.getResponseHeader("Last-Modified");
if ( modified ) {
jQuery.lastModified[ cacheURL ] = modified;
}
modified = jqXHR.getResponseHeader("etag");
if ( modified ) {
jQuery.etag[ cacheURL ] = modified;
}
}
// if no content
//204表示没有数据,这时候页面就不需要跳转!还是停留在当前页面!
if ( status === 204 || s.type === "HEAD" ) {
statusText = "nocontent";
//如果是304那么表示没有修改内容!
// if not modified
} else if ( status === 304 ) {
statusText = "notmodified";
//如果有数据,那么我们获取到数据!
// If we have data, let's convert it
} else {
statusText = response.state;
success = response.data;
error = response.error;
isSuccess = !error;
}
} else {
//这里的else表示请求失败!我们从statusText获取到错误的信息,然后对statusText进行处理!
// We extract error from statusText
// then normalize statusText and status for non-aborts
error = statusText;
if ( status || !statusText ) {
statusText = "error";
if ( status < 0 ) {
status = 0;
}
}
}
//为jqXHR设置数据
// Set data for the fake xhr object
jqXHR.status = status;
jqXHR.statusText = ( nativeStatusText || statusText ) + "";
// Success/Error
if ( isSuccess ) {
//如果成功了请求,那么我们传入的Context是callbackContext,传入的数据是response.data
//response.status和jqXHR对象
deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] );
} else {
deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] );
}
// Status-dependent callbacks
jqXHR.statusCode( statusCode );
statusCode = undefined;
//如果是全局执行
if ( fireGlobals ) {
globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError",
[ jqXHR, s, isSuccess ? success : error ] );
}
// Complete
//这个对象添加的所有函数执行,表示完成,不是成功,失败,而是complelte表示不管成功与否都是会执行的!
//而且只会执行一次!
completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] );
if ( fireGlobals ) {
//globalEventContext也就是最终options的事件,触发事件ajaxComplete!
globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] );
// Handle the global AJAX counter
//如果全局的ajax计数器已经是0了,那么就会触发ajaxStrop事件!
if ( !( --jQuery.active ) ) {
jQuery.event.trigger("ajaxStop");
}
}
}
// Attach deferreds
// 开发网站的过程中,我们经常遇到某些耗时很长的javascript操作。其中,既有异步的操作(比如ajax读取服务器数据),也有同步的操作(比如遍历一个大型数组),它们都不是立即能得到结果的。
// 通常的做法是,为它们指定回调函数(callback)。即事先规定,一旦它们运行结束,应该调用哪些函数。
// deferred对象就是jQuery的回调函数解决方案 '延迟执行'
//让jqXHR具有promise的所有的属性和方法!不包括状态改变的方法,如resollveWith等
//同时jqXHR的complete对应于completeDeferred的add方法,但是该jqXHR中也封装了三个Callbacks对象
//但是这里没有用内部的Callbacks对象,而是采用一个新的Callbacks对象
//completeDeferred = jQuery.Callbacks("once memory"),
deferred.promise( jqXHR ).complete = completeDeferred.add;
//success调用的promise对象内置的done方法对应于的Callbacks对象
jqXHR.success = jqXHR.done;
//error方法调用的promise对象内置的fail方法对应的Callbacks对象!
//注意:这个内置的promise对象的progress方法对应的Callbacks对象没有用到!
jqXHR.error = jqXHR.fail;
// 处理url 去掉hash地址和添加协议
// 例如 var c="//brandshow.58.com/show/loadBrandAds#top"
// c.replace(rhash, "").replace(rprotocol, ajaxLocParts[1] + "//");
// 结果"http://brandshow.58.com/show/loadBrandAds"-->
s.url = ( ( url || s.url || ajaxLocation ) + "" ).replace( rhash, "" ).replace( rprotocol, ajaxLocParts[ 1 ] + "//" );
//type就是get或者post。这个方式可以通过用户传入的对象的method或者type或者最终的对象的method或者type获取!
// Alias method option to type as per ticket #12004
s.type = options.method || options.type || s.method || s.type;
// Extract dataTypes list
//取出dataType两边的空格,同时通过空格进行分组得到一个数组!dataType="html"
s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().match( rnotwhite ) || [ "" ];
//如果没有crossDomain对象
// A cross-domain request is in order when we have a protocol:host:port mismatch
if ( s.crossDomain == null ) {
parts = rurl.exec( s.url.toLowerCase() );
//parts返回值(以数组形式包含协议 域名 端口号;然后分别和当前地址的协议 域名 端口号比较,有一处不等则判断为跨域 )
// 例如:["http://api.house.58.com", "http:", "api.house.58.com", undefined]
//如果在同一个域名里面那么这里的判断都是false,结果就是crossDomain为false
//如果不再同一个域名里面,那么这里的判断都是true,结果就是crossDomain为true!
s.crossDomain = !!( parts &&
( parts[ 1 ] !== ajaxLocParts[ 1 ] || parts[ 2 ] !== ajaxLocParts[ 2 ] ||
( parts[ 3 ] || ( parts[ 1 ] === "http:" ? "80" : "443" ) ) !==
( ajaxLocParts[ 3 ] || ( ajaxLocParts[ 1 ] === "http:" ? "80" : "443" ) ) )
);
}
//如果存在data同时存在processData同时data不是string!
//traditional是为了兼容jQuery<1.3.2行为的!
// Convert data if not already a string
if ( s.data && s.processData && typeof s.data !== "string" ) {
s.data = jQuery.param( s.data, s.traditional );
}
// Apply prefilters
// prefilters前置过滤器
// jQuery1.5以后,AJAX模块提供了三个新的方法用于管理、扩展AJAX请求,分别是:
// 1.前置过滤器 jQuery. ajaxPrefilter
// 2.请求分发器 jQuery. ajaxTransport,
// 3.类型转换器 ajaxConvert
// prefilters对象 Object {json: Array[1], jsonp: Array[1], script: Array[1]}
<!--前置过滤器主要预处理参数
举例 dataType:json或者jsonp 会一次经过jQuery. ajaxPrefilter("json jsonp")和jQuery. ajaxPrefilter("script")
结果:url会拼接上s.jsonp+jsonpcallback(如?callback=jQuery4781297478394_481274389)
s.cache=false;
如果是跨域
s.type = "GET";
s.global = false;
-->
//参见文章ajax源码解析-jQuery. ajaxPrefilter和jQuery. ajaxTransport
inspectPrefiltersOrTransports( prefilters, s, options, jqXHR );
//如果在预过滤器中已经终止了请求,那么直接返回jqXHR对象!
// If request was aborted inside a prefilter, stop there
if ( state === 2 ) {
return jqXHR;
}
// We can fire global events as of now if asked to
// Don't fire events if jQuery.event is undefined in an AMD-usage scenario (#15118)
//如果是global参数,那么我们直接trigger事件ajaxStart!
fireGlobals = jQuery.event && s.global;
// Watch for a new set of requests
if ( fireGlobals && jQuery.active++ === 0 ) {
jQuery.event.trigger("ajaxStart");
}
// Uppercase the type
//把type变成大写
s.type = s.type.toUpperCase();
// Determine if request has content
//rnoContent = /^(?:GET|HEAD)$/
//也就是如果没有指定type也就是请求方式!
s.hasContent = !rnoContent.test( s.type );
// Save the URL in case we're toying with the If-Modified-Since
// and/or If-None-Match header later on
//获取url参数!
cacheURL = s.url;
// More options handling for requests with no content
//如果指定了请求方式,如get,post等!
if ( !s.hasContent ) {
//没有指定请求方式的时候有传递数据!
// If data is available, append data to url
if ( s.data ) {
//var rquery = (/\?/);
//如果url后面有问号,那么直接把参数绑定到问号后面就可以了!否则添加问号在绑定!
//同时删除数据!
cacheURL = ( s.url += ( rquery.test( cacheURL ) ? "&" : "?" ) + s.data );
// #9682: remove data so that it's not used in an eventual retry
delete s.data;
}
// Add anti-cache in url if needed
//如果指定了cache为false表示不能进行数据缓存,那么会在url后面添加一个当前时间!
if ( s.cache === false ) {
s.url = rts.test( cacheURL ) ?
// If there is already a '_' parameter, set its value
//var nonce = jQuery.now();
cacheURL.replace( rts, "$1_=" + nonce++ ) :
// Otherwise add one to the end
cacheURL + ( rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + nonce++;
}
}
// Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
//如果添加了ifModified头
//var lastModified={}
if ( s.ifModified ) {
//如果lastModified保存了这个cacheURL也就是这个URL有缓存了!那么直接添加头If-Modified-Since数据为
//jQuery.lastModified[ cacheURL ]获取到的数据!
if ( jQuery.lastModified[ cacheURL ] ) {
jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] );
}
//如果在etag: {}中保存了这个URL
//那么添加If-None-Match,因为Etag和if-None-Match是一对,Last-Modified和If-Modified-Since是一对!
if ( jQuery.etag[ cacheURL ] ) {
jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] );
}
}
// Set the correct header, if data is being sent
//如果有数据传送,同时也指定了get,post方法,同时contentType也指定!
//那么添加一个头Content-Type!
if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) {
jqXHR.setRequestHeader( "Content-Type", s.contentType );
}
// Set the Accepts header for the server, depending on the dataType
//同时添加请求头Accept
//(1)如果指定了dataType,同时accepts中dataType存在,也就是必须是指定的data[type]
jqXHR.setRequestHeader(
"Accept",
s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[0] ] ?
// allTypes = "*/".concat("*");
//如果支持的数据类型是内置的类型,那么获取内置的值,如text获取的就是"text/plain"
//同时dataTypes[0]不是"*",那么我们加上一个逗号,同时加上后面剩余的部分!
/*
var allTypes = "*/".concat("*");
//打印
//alert(allTypes);
//最后的格式就是:text/html,*/*;q=0.01
//如果传入的dataType就是*,那么最后的结果就是"*/*"
s.accepts[ s.dataTypes[0] ] + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) :
s.accepts[ "*" ]
);
// Check for headers option
//如果还定义了headers选项,那么会被逐个发送到服务器端!
for ( i in s.headers ) {
jqXHR.setRequestHeader( i, s.headers[ i ] );
}
// Allow custom headers/mimetypes and early abort
//如果指定了beforeSend,同时beforeSend的函数调用的结果是false或者state是2,那么取消这次请求
//beforeSend中传入的参数为callbackContext = s.context || s也就是最终对象的context参数为上下文
//第一个参数是XHR对象,第二个参数是最终的options对象!
if ( s.beforeSend && ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || state === 2 ) ) {
// Abort if not done already and return
return jqXHR.abort();
}
// aborting is no longer a cancellation
strAbort = "abort";
// Install callbacks on deferreds
//往jqXHR["success"]和jqXHR["error"],jqXHR["complete"]中添加回调函数
//回调函数就是通过最终options对象获取到的success,error,complete函数!
for ( i in { success: 1, error: 1, complete: 1 } ) {
jqXHR[ i ]( s[ i ] );
}
// Get transport
//传入的参数是transports对象!这个函数里面会判断是否传入了transports
transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR );
// If no transport, we auto-abort
if ( !transport ) {
done( -1, "No Transport" );
} else {
//如果有transport那么readyState就是1,表示 (载入)已调用send()方法,正在发送请求,也就是请求的发送是在
//inspectPrefiltersOrTransports里面完成的!
jqXHR.readyState = 1;
// Send global event
//指示是否触发全局Ajax事件。将该值设为false将阻止全局事件处理函数被触发
//fireGlobals = jQuery.event && s.global;
//如果是表示全局ajax事件,那么我们要调用ajaxSend方法!同时为这个方法传入参数jqXHR和最终option!
if ( fireGlobals ) {
globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] );
}
// Timeout
//如果指定了async同时timeout>0表示指定了隔多少秒就放弃
//一个超时调用,超时直接调用abort方法!
if ( s.async && s.timeout > 0 ) {
timeoutTimer = setTimeout(function() {
jqXHR.abort("timeout");
}, s.timeout );
}
//如果有transport,那么调用send方法!
try {
state = 1;
<!--经过请求分发器的处理 返回的对象格式一致(不论是跨域请求还是非跨域请求) 即{send:{},abort:{}}-->
transport.send( requestHeaders, done );
} catch ( e ) {
// Propagate exception as error if not done
if ( state < 2 ) {
done( -1, e );
// Simply rethrow otherwise
} else {
throw e;
}
}
}
sunbaixin wechat
欢迎您扫一扫上面的微信公众号,订阅我的博客!