jquery.ajax源码
框架的作用就是简化我们做的事情,却又不失灵活性。jquery是js框架中的中流砥柱,灵活并且强大。
jquery中对ajax的封装很完美,且不说底层的ajax函数的强大,但是其上层的get ,post ,getscript ,getJson 基本对各种应用游刃有余。为什么要看源码,一是闲着蛋疼,二是为了在出问题时,能找出问题所在。三是……。
jquery中有一个对象ajaxSeetings ,ajax请求最基本的配置就在这里,结构如下
ajaxSettings: { url: location.href, global: true, type: "GET", contentType: "application/x-www-form-urlencoded", processData: true, async: true, /* timeout: 0, data: null, username: null, password: null, traditional: false, */ // Create the request object; Microsoft failed to properly // implement the XMLHttpRequest in IE7 (can't request local files), // so we use the ActiveXObject when it is available // This function can be overriden by calling jQuery.ajaxSetup xhr: window.XMLHttpRequest && (window.location.protocol !== "file:" || !window.ActiveXObject) ? function() { return new window.XMLHttpRequest(); } : function() { try { return new window.ActiveXObject("Microsoft.XMLHTTP"); } catch(e) {} }, accepts: { xml: "application/xml, text/xml", html: "text/html", script: "text/javascript, application/javascript", json: "application/json, text/javascript", text: "text/plain", _default: "*/*" } }
基本上名字就能代表它的配置项目,processData可能比较陌生。我们在使用get以及其他上层函数请求资源时,传递一个key/value的对象。例如$.get(“xxxx”,{name:’pr’,password:’pr’} , ……); 如果process设置为true,{name:’pr’,password:’pr’}就会转换为name=pr&password=pr;这样在后面如果ajax方式为get则会将装换的字符串附加到url后面,如果设置为false则不进行此转换,默认是true,也最好不要改。值得一看内容当然是属性xhr,这个属性是个函数,当然函数最后都会返回浏览器兼容的XmlHttpRequest对象。整个ajax的核心操作对象就是它,这就免去了我们自己构造XmlHttpRequest对象时考虑兼容问题的纠结。
ajax: function( origSettings ),ajax接受一个配置对象,就跟上面的ajaxSettings那样的结构,
var s = jQuery.extend(true, {}, jQuery.ajaxSettings, origSettings); var jsonp, status, data, callbackContext = origSettings && origSettings.context || s, type = s.type.toUpperCase(); // convert data if not already a string if ( s.data && s.processData && typeof s.data !== "string" ) { s.data = jQuery.param( s.data, s.traditional ); }
首先函数将默认配置和传进来的配置进行合并,在函数中注意有个{},这样合并就不会影响ajaxSettings 和originSettings的本来的值。CallbackContext是执行ajax回调函数是函数的上下文。其他不多说。然后根据data,ProcessData 和data是否是string来决定是不是要将data对象转换为参数形式字符串。jquery.param是个工具函数,traditional用来决定是不是要进行深层次遍历以生成参数字符串。具体事例见jquery文档。
// Handle JSONP Parameter Callbacks if ( s.dataType === "jsonp" ) { if ( type === "GET" ) { if ( !jsre.test( s.url ) ) { s.url += (rquery.test( s.url ) ? "&" : "?") + (s.jsonp || "callback") + "=?"; } } else if ( !s.data || !jsre.test(s.data) ) { s.data = (s.data ? s.data + "&" : "") + (s.jsonp || "callback") + "=?"; } s.dataType = "json"; } // Build temporary JSONP function if ( s.dataType === "json" && (s.data && jsre.test(s.data) || jsre.test(s.url)) ) { jsonp = s.jsonpCallback || ("jsonp" + jsc++); // Replace the =? sequence both in the query string and the data if ( s.data ) { s.data = (s.data + "").replace(jsre, "=" + jsonp + "$1"); } s.url = s.url.replace(jsre, "=" + jsonp + "$1"); // We need to make sure // that a JSONP style response is executed properly s.dataType = "script"; // Handle JSONP-style loading window[ jsonp ] = window[ jsonp ] || function( tmp ) { data = tmp; success(); complete(); // Garbage collect window[ jsonp ] = undefined; try { delete window[ jsonp ]; } catch(e) {} if ( head ) { head.removeChild( script ); } }; }
接下来特殊处理请求数据类型为jsonp,jsonp其实是一种非官方的协议,主要就是跨域的访问。在后面你可以看到它会和请求类型为‘script’相似处理。
如果jsonp大家不熟悉的话,可以去网上查找相关资料,或直接跳过jsonp,不影响后面的阅读。
jsre是一个正则表达式 jsre = /=\?(&|$)/ ,他主要又来匹配‘=?’这个其实是jsonp请求中独有的字符串,如果url中没有对应的字符,则在后面加上jsonp请求独有的字符串。requery同样也是一个正则表达式/\?/ ,就是用来匹配问号的,如果原先url含有?,则说明url中带有参数,则连接字符使用&,否则用?。如果配置对象中含有jsonp,则指定了jsonp的回调函数名,否则使用默认回调函数名callback。若果ajax请求采用post方式,则只需对配置对象中的data进行拼接字符串即可。datatype设置为json是为了进一步的细化处理。无论是在get方式还是其他方式,在处理前都会用jsre匹配一下,只有在确保字符串中没有jsonp的特征字符串’=?‘时才会就行处理,也就是说不能有两个jsonp在一起(自己的一点瞎想,欢迎大家讨论)。
接下来构建jsonp回调函数。因为前文说过没有指定jsonp属性的话是默认为Callback。如果指定了jsonpCallback则直接用但是没有的就要构造构造一个独一无二的回调函数名,用什么呢,除了时间还有跟好的解决方法吗?jsc就是当前时间 ,jsc =now(); 然后用此回调函数名换掉?,这样就符合了参数字符串的格式。
window[jsonp],为回调函数注册。
if ( s.dataType === "script" && s.cache === null ) { s.cache = false; } if ( s.cache === false && type === "GET" ) { var ts = now(); // try replacing _= if it is there var ret = s.url.replace(rts, "$1_=" + ts + "$2"); // if nothing was replaced, add timestamp to the end s.url = ret + ((ret === s.url) ? (rquery.test(s.url) ? "&" : "?") + "_=" + ts : ""); } // If data is available, append data to url for get requests if ( s.data && type === "GET" ) { s.url += (rquery.test(s.url) ? "&" : "?") + s.data; } // Watch for a new set of requests if ( s.global && ! jQuery.active++ ) { jQuery.event.trigger( "ajaxStart" ); } // Matches an absolute URL, and saves the domain var parts = rurl.exec( s.url ), remote = parts && (parts[1] && parts[1] !== location.protocol || parts[2] !== location.host); // If we're requesting a remote document // and trying to load JSON or Script with a GET if ( s.dataType === "script" && type === "GET" && remote ) { var head = document.getElementsByTagName("head")[0] || document.documentElement; var script = document.createElement("script"); script.src = s.url; if ( s.scriptCharset ) { script.charset = s.scriptCharset; } // Handle Script loading if ( !jsonp ) { var done = false; // Attach handlers for all browsers script.onload = script.onreadystatechange = function() { if ( !done && (!this.readyState || this.readyState === "loaded" || this.readyState === "complete") ) { done = true; success(); complete(); // Handle memory leak in IE script.onload = script.onreadystatechange = null; if ( head && script.parentNode ) { head.removeChild( script ); } } }; } // Use insertBefore instead of appendChild to circumvent an IE6 bug. // This arises when a base node is used (#2709 and #4378). head.insertBefore( script, head.firstChild ); // We handle everything using the script element injection return undefined; }
进行ajax进行请求是可能会会有缓存文件(当然着仅存在请求方式为Get时,关于Get和post在ajax请求时的具体区别请参考w3school中的介绍),当请求格式为‘script’(可能是后来转换的如jsonp)时,则没有缓存文件,
补充:Web开发 , Jsp ,