当前位置: 首页 > 工具软件 > jQuery Screw > 使用案例 >

jquery ajax发送前处理,jQuery ajax 源码分析之预处理和分发函数(prefilter/transport)

司马高明
2023-12-01

调用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 requests

jQuery.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 set

if ( 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("").prop({

async: true,

charset: s.scriptCharset,

src: s.url

}).on(

// 如果加载错误的情况下,就把创建的script移除,并返回结果

"load error",

callback = function( evt ) {

script.remove();

callback = null;

if ( evt ) {

complete( evt.type === "error" ? 404 : 200, evt.type );

}

}

);

document.head.appendChild( script[ 0 ] );

},

// 如果中途撤销 ajax,如果有callback就执行callback函数

abort: function() {

if ( callback ) {

callback();

}

}

};

}

});

总共有两处使用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();

}

}

};

}

});

 类似资料: