博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
jQuery ajax 源码分析之预处理和分发函数(prefilter/transport)
阅读量:5912 次
发布时间:2019-06-19

本文共 7077 字,大约阅读时间需要 23 分钟。

调用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 函数})

重点需要介绍的函数有三个ajaxPrefilterajaxTransport , ajax

ajaxPrefilter 函数通过addToPrefiltersOrTransports( prefilters ) 实现的,看下jQuery 通过ajaxPrefilter 做了哪些操作

  • jQuery.ajaxPrefilter( "script", function( s ) {}) 如果dataTypescript 的话,设置缓存操作,如果是跨域的话,type 就必须设置为get
  • jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) {}) 如果dataTypejson 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();                }            }        };    }});

转载地址:http://bxmpx.baihongyu.com/

你可能感兴趣的文章
定时器
查看>>
本周末预计更新博客内容
查看>>
Haskell写的Parser
查看>>
mysql触发器
查看>>
python练习-统计包含数字字符串元组在内的列表内数据类型个数
查看>>
AngularJS(5)-Http
查看>>
做个CMS吧(二)-Forms登陆验证
查看>>
整行读字符串且需分割计算字符串个数
查看>>
这是我的第一篇博客
查看>>
sql事务、视图和索引
查看>>
Java基础之Java常用类--Object类,字符串相关类,包装类,日期相关类,数字相关类...
查看>>
创建正真的Java不可变类
查看>>
创建Java不可变型的枚举类型Gender
查看>>
lnmp 的监控----cacti
查看>>
【转载】wpf学习笔记2
查看>>
微软新技术学术交流中心
查看>>
结对开发——求环形二维数组最大子矩阵和的问题
查看>>
软件工程结队项目——智能点餐系统典型用户及用户场景分析
查看>>
linux 2>&1的用法
查看>>
敏捷开发与瀑布式开发的区别
查看>>