Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2877979
  • 博文数量: 471
  • 博客积分: 7081
  • 博客等级: 少将
  • 技术积分: 5369
  • 用 户 组: 普通用户
  • 注册时间: 2012-01-04 21:55
文章分类

全部博文(471)

文章存档

2014年(90)

2013年(69)

2012年(312)

分类: Web开发

2013-05-21 09:29:25

Jquery源码分析---Ajax分析。

  7.1 jquery的ajax常用方法

  对于Ajax原理不深入分析。Jquery肯定也会提供Ajax的实现。对于ajax的请求,可以分成如下的几步:

  1、通过 new XMLHttpRequest 或其它的形式(指IE)生成ajax的对象xhr。

  2、通过xhr.open(type, url, async, username, password)的形式建立一个连接。

  3、通过setRequestHeader设定xhr的请求头部(request header)。

  4、通过send(data)请求服务器端的数据。

  5、执行在xhr上注册的onreadystatechange回调处理返回数据。

  任何的lib都是在这几步之上进行相关扩展而达到自己的功能。这几步之中,对于url,我们要考虑是跨域请求怎么办,ajax为了安全的考虑不支持跨域请求,那么对于这个问题,Jquery和Ext都是采用scriptTag的方式。

  Jquery主要就是解决上面这问题,之后就在这个基础之上进行扩展,如getScript,getJson之类的函数。Ajax一个很重要的任务就是提交form。Jquery Ajax提供了如Prototype的form中serializeElements把form的元素串行起请求字符串。这是ajax的辅助方法。

  对于ajax的应用,不管对返回的数据进行如何的处理,其最终目的还是得落在页面的显示上,也是某一个或一些dom元素上。那么我从这个需要改变内容的dom元素(集合)出发,通过ajax去获得数据进行一些处理最终填充到给定的元素中。这和prototype中Ajax.Updata相似。

  load

  Jquery对象的load(url, params, callback)函数就是完成这类似的工作的。

// 载入远程 HTML 文件代码并插入至 DOM 中。
// 默认使用 GET 方式 - 传递附加参数时自动转换为 POST 方式。jQuery 1.2 中,
//可以指定选择符,来筛选载入的 HTML 文档,DOM 中将仅插入筛选出的 HTML 代码。
// 语法形如 "url #some > selector"。
load : function(url, params, callback) {
  if (typeof url != 'string')   return this._load(url);
  var off = url.indexOf(" ");// 找到第一个空格处
  if (off >= 0) {                          ①
    var selector = url.slice(off, url.length);// 第一个空格之后的字符
    url = url.slice(0, off);// 第一个空格之前的字符
    }
callback = callback || function() {
  };
var type = "GET";// 默认的是get类型
// 这里是判断第二参数,如果是fn,那么就是指callback
// 如果是object,那么就是指data.load(url,[data],[callback])
if (params)                             
  if (jQuery.isFunction(params)) {
    callback = params;  params = null;}
else if (typeof params == 'object') {
    params = jQuery.param(params);type = "POST";    ②
  }
var self = this;    
jQuery.ajax( {//Ajax请求                     ③
url : url, type : type,dataType : "html",data : params,
complete : function(res, status) {
    // 成功就注射html到所有匹配的元素中
  if (status == "success" || status == "notmodified")
    // selector是否指定?没有的话就是全部的内容
    // 指定的话,就是生成dom文档的形式,之后在中间找到满足条件的元素。
    // 这中间删除 scripts 是避免IE中的 'Permission Denied' 错误
self.html(selector ? jQuery("

")           ④
  .append(res.responseText.replace(//g, ""))
      .find(selector) : res.responseText); 
self.each(callback, [res.responseText, status, res]); // 执行回调⑤
  }
});
return this;
   },

关键字: FCKEditor 闭包 Dojo 在线编辑器 position

Jquery源码分析---Ajax分析。

  上面的代码①可以看出load的url参数可以指定选择符,来筛选载入的 HTML 文档,DOM 中将仅插入筛选出的 HTML 代码。 语法形如 "url #some > selector"。在④处通过html的方式插入到元素的内部(取代)。其支持的response应该是html的片断。

  param

  在②处通过调用了jQuery.param(params)来完成对params的key/value的对象形式转换成查寻字符串的形式。

// 串行化form子元素组成的数组或对象形式查询字符串
  param : function(a) {
    var s = [];
    function add(key, value) {
      s[s.length] = encodeURIComponent(key) + '='
         + encodeURIComponent(value);
    };
/ 对于数组的参数,每个元素({name:xx,value:yy})都串行化为key/value的字符串。
    if (a.constructor == Array || a.jquery)
      jQuery.each(a, function() {
       add(this.name, this.value);
      });
    // 对于对象{a1:{name:xx,value:yy},a2:{name:xx,value:yy}}
    // 都串行化为key/value的字符串。
    else
      for (var j in a)
       // value是数组,key 名字要重复
       if (a[j] && a[j].constructor == Array)
         jQuery.each(a[j], function() {
           add(j, this);
         });
       else
         add(j, jQuery.isFunction(a[j]) ? a[j]() : a[j]);
    // 返回生成字符串
    return s.join("&").replace(/%20/g, "+");
  }

关键字: FCKEditor 闭包 Dojo 在线编辑器 position

Jquery源码分析---Ajax分析。

  Get&post

  Load有的时候也不能很好地完成功能,如果不是html的response。那么不能采用了。Jquery还提供了几个静态方式:jquery.get()、jquery. getScript ()、jquery. getJSON ()、jquery. post ()。jquery. getScript ()、jquery. getJSON ()不过是在jquery.get()基础之上提供了某方面的简单的处理。Get和Post在这里没有什么不一样,除了请求的type不一样之外。

//通过get的type方式进行ajax的请求
  get : function(url, data, callback, type) {
    // 前移 arguments 如data 参数省略
    if (jQuery.isFunction(data)) {
      callback = data; data = null; }
    return jQuery.ajax( {  type : "GET", url : url,
      data : data, success : callback, dataType : type
    });
  },
 //以post方式进行ajax请求
post : function(url, data, callback, type) {
    if (jQuery.isFunction(data)) {
      callback = data; data = {}; }
    return jQuery.ajax( {type : "POST",url : url,
      data : data, success : callback,dataType : type   });
  },

  Get和post都是在ajax的请求方面没有什么区别。除了在服务器的接收处理的方面有所不同之外。如get在.net中是request.querstring,而post则是采用request.form来获得。

  //取得返回的script
  getScript : function(url, callback) {
    return jQuery.get(url, null, callback, "script");
  },
  //取得json
  getJSON : function(url, data, callback) {
    return jQuery.get(url, data, callback, "json");
  },

关键字: FCKEditor 闭包 Dojo 在线编辑器 position

Jquery源码分析---Ajax分析。

  getScript、getJSON都是在调用 jQuery.get,传入不同的datatype的形式来区别不同的类型。

  7.2 jquery.ajax

  这所有的最终都是通过jQuery.ajax()来完成的。

ajax : function(s) {
  //两次继承s,以便在测试中能检测
  s = jQuery.extend(true, s, jQuery.extend(true, {},
jQuery.ajaxSettings, s));     ①
  var jsonp, jsre = /=?(&|$)/g, status, data,
type = s.type .toUpperCase();
// 如果不是字符集串就转换在查询字符集串
if (s.data && s.processData && typeof s.data != "string")
    s.data = jQuery.param(s.data);
  
// 构建jsonp请求字符集串。jsonp是跨域请求,要加上callback=?后面将会加函数名
if (s.dataType == "jsonp") {                ②
if (type == "GET") {//使get的url包含 callback=?后面将会进行加函数名
    if (!s.url.match(jsre))
      s.url += (s.url.match(/?/) ? "&" : "?")
             + (s.jsonp || "callback") + "=?";
      } // 构建新的s.data,使其包含callback=function name
else if (!s.data || !s.data.match(jsre))
    s.data = (s.data ? s.data + "&" : "") + (s.jsonp||"callback")+ "=?";
s.dataType = "json";
}  
//判断是否为jsonp,如果是,进行处理。
if (s.dataType == "json" 
      && (s.data && s.data.match(jsre) || s.url.match(jsre))) {③
   jsonp = "jsonp" + jsc++;
/ /为请求字符集串的callback=加上生成回调函数名
  if (s.data)s.data = (s.data + "").replace(jsre, "=" + jsonp + "$1");
  s.url = s.url.replace(jsre, "=" + jsonp + "$1");
  
      // 我们需要保证jsonp 类型响应能正确地执行
  //jsonp的类型必须为script。这样才能执行服务器返回的
  //代码。这里就是调用这个回调函数。
  s.dataType = "script";
//window下注册一个jsonp回调函数有,让ajax请求返回的代码调用执行它,
  //在服务器端我们生成的代码 如callbackname(data);形式传入data.
  window[jsonp] = function(tmp) {
    data = tmp;success();complete();
  // 垃圾回收,释放联变量,删除jsonp的对象,除去head中加的script元素
    window[jsonp] = undefined;
    try { delete window[jsonp];
      } catch (e) { }
    if (head) head.removeChild(script);
    };
  }
  
if (s.dataType == "script" && s.cache == null)  s.cache = false;
// 加上时间戳,可见加cache:false就会加上时间戳
if (s.cache === false && type == "GET") {
  var ts = now();
  var ret = s.url.replace(/(?|&)_=.*?(&|$)/, "$1_=" + ts + "$2");
  // 没有代替,就追加在url的尾部
  s.url = ret+ ((ret == s.url) ? (s.url.match(/?/) ? "&" : "?") + "_="
           + ts : "");
  }
// data有效,追加到get类型的url上去
if (s.data && type == "GET") {
    s.url += (s.url.match(/?/) ? "&" : "?") + s.data;
    // 防止IE会重复发送get和post data
    s.data = null;
    }
// 监听一个新的请求
if (s.global && !jQuery.active++) jQuery.event.trigger("ajaxStart");④
// 监听一个绝对的url,和保存domain
var parts = /^(w+:)?//([^/?#]+)/.exec(s.url);
// 如果我们正在请求一个远程文档和正在load json或script通过get类型
//location是window的属性,通过和地址栏中的地址比较判断是不是跨域。
if (s.dataType == "script" && type == "GET"&& parts && (parts[1] &&
parts[1] != location.protocol || parts[2] != location.host)) {⑤
    // 在head中加上
    var head = document.getElementsByTagName("head")[0];
    var script = document.createElement("script");
    script.src = s.url;
    if (s.scriptCharset) script.charset = s.scriptCharset;
    //如果datatype不是jsonp,但是url却是跨域的。采用scriptr的
    //onload或onreadystatechange事件来触发回调函数。
    if (!jsonp) {
      var done = false;
      // 对所有浏览器都加上处理器
      script.onload = script.onreadystatechange = function() {
      if (!done&& (!this.readyState || this.readyState == "loaded"
|| this.readyState == "complete")) {
         done = true; success();
         complete();head.removeChild(script);
       }
    };
  }
  head.appendChild(script);
// 已经使用了script 元素注射来处理所有的事情
  return undefined;
}
var requestDone = false;
// 创建request,IE7不能通过XMLHttpRequest来完成,只能通过ActiveXObject
var xhr = window.ActiveXObject                ⑥
    ? new ActiveXObject("Microsoft.XMLHTTP"): new XMLHttpRequest();
// 创建一个请求的连接,在opera中如果用户名为null会弹出login窗口中。
if (s.username)xhr.open(type, s.url, s.async, s.username, s.password);
else  xhr.open(type, s.url, s.async);
// try/catch是为防止FF3在跨域请求时报错
try {// 设定Content-Type                       ⑦
  if (s.data)
    xhr.setRequestHeader("Content-Type", s.contentType);
    // 设定If-Modified-Since
  if (s.ifModified)
    xhr.setRequestHeader("If-Modified-Since",
      jQuery.lastModified[s.url]|| "Thu, 01 Jan 1970 00:00:00 GMT");
// 这里是为了让服务器能判断这个请求是XMLHttpRequest
  xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
// 设定 Accepts header 。指能接收的content-type,在服务器端设定
  xhr.setRequestHeader("Accept", s.dataType && s.accepts[s.dataType]
      ? s.accepts[s.dataType] + ", */*": s.accepts._default);
} catch (e) {}
//拦截方法,我们可以在send之前进行拦截。返回false就不send
if (s.beforeSend && s.beforeSend(xhr, s) === false) {     ⑧
  // 清除active 请求计数
  s.global && jQuery.active--;
  xhr.abort();
  return false;
}
  
// 触发全局的ajaxSend事件
if (s.global) jQuery.event.trigger("ajaxSend", [xhr, s]);
// 等待response返回,主要是为后面setInterval用。
var onreadystatechange = function(isTimeout) {      ⑨
// 接收成功或请求超时
if (!requestDone && xhr&& (xhr.readyState == 4 ||isTimeout == "timeout")) { requestDone = true;
    //清除定时器
    if (ival) {clearInterval(ival);  ival = null; }
  // 分析status:tiemout-->error-->notmodified-->success
  status = isTimeout == "timeout" ? "timeout" : !jQuery
    ttpSuccess(xhr) ? "error" : s.ifModified&& jQuery.
httpNotModified(xhr, s.url) ? "notmodified": "success";
  //如果success且返回了数据,那么分析这些数据
  if (status == "success") {        
    try { data = jQuery.httpData(xhr, s.dataType, s);
      } catch (e) { status = "parsererror"; }
  }
// 分析数据成功之后,进行last-modified和success的处理。     
  if (status == "success") {
    var modRes;
     try {modRes = xhr.getResponseHeader("Last-Modified");
      } catch (e) { //FF中如果head取不到,会抛出异常}
    //保存last-mordified的标识。
    if (s.ifModified && modRes)jQuery.lastModified[s.url] = modRes;
    // JSONP 有自己的callback
    if (!jsonp) success();
  } else // 失败时的处理
  jQuery.handleError(s, xhr, status);
// 无论如何都进行cpmplate.timeout和接收成功
  complete();
if (s.async) xhr = null; // 防内存泄漏
}
};
if (s.async) {
// 这里是采用poll的方式,不是push的方式
//这里为什么不采用onreadystatechange?
var ival = setInterval(onreadystatechange, 13);
//如果过了timeout还没有请求到,会中断请求的。
  if (s.timeout > 0)
    setTimeout(function() {        
      if (xhr) { xhr.abort();
       if (!requestDone) onreadystatechange("timeout"); }
    }, s.timeout);
  }
// 发送
try {xhr.send(s.data); catch(e){jQuery.handleError(s,xhr,null,e);} ⑩
// firefox 1.5 doesn't fire statechange for sync requests
if (!s.async) onreadystatechange();
function success() {
  // 调用构建请求对象时指定的success回调。
  if (s.success)  s.success(data, status);
  // 执行全局的回调
  if (s.global) jQuery.event.trigger("ajaxSuccess", [xhr, s]);
  }
function complete() {
  // 本地的回调
  if (s.complete)  s.complete(xhr, status);
  // 执行全局的回调
  if (s.global) jQuery.event.trigger("ajaxComplete", [xhr, s]);
  // 全局的ajax计数器
  if (s.global && !--jQuery.active)jQuery.event.trigger("ajaxStop");
  }
// return XMLHttpRequest便进行about()或其它操作.
return xhr;
},

关键字: FCKEditor 闭包 Dojo 在线编辑器 position

Jquery源码分析---Ajax分析。

  Jquery.ajax是大包大揽的非常复杂的一个方法。它并没有像其它的lib一样,把每个小部分都分开来。它是整个都整在一个函数中。看起来很多,实际上上也没有脱离前面所说的ajax的请求的五步。它的很大一部分代码在处理跨域请求的处理上。下面就分别就ajax的代码进行分析。

  ajaxSettings

  在①处通过继承的方式把传入参数s和默认的jQuery.ajaxSettings都clone到s变量中。S的同名属性会覆盖jQuery.ajaxSettings的同名属性。这里两次继承s,以便在测试中能检测。

//默认的ajax的请求参数
  ajaxSettings : {
    url : location.href,//默认是地址栏中url
    global : true,//默认支持全局的ajax事件
    type : "GET",
    timeout : 0,
    contentType : "application/x-www-form-urlencoded", 
    processData : true,
    async : true,
    data : null,
    username : null,
    password : null,
    accepts : {
      xml : "application/xml, text/xml",
      html : "text/html",
      script : "text/javascript, application/javascript",
      json : "application/json, text/javascript",
      text : "text/plain",
      _default : "*/*"
   }

  这是默认的ajax的设定,我们要在参数s设定同名的属性来覆盖这些属性。但是我们不能覆盖accepts。这个会在后面的代码用到。我们可以通过设定s.dataType等于accepts中的某一个属性key指定请求的data类型,如xml,html,script,json,text。dataType还支持默认的_default和跨域的jsonp。不过其最终会解析成script。

关键字: FCKEditor 闭包 Dojo 在线编辑器 position

Jquery源码分析---Ajax分析。

  scriptTag

  ②~⑥是处理跨域请求的部分。对于dataType为jsonp的类型,给其请求的字符串(可能是s.data)加上callback=callbackfn的key/value串,然后在window下注册一个callbackfn的函数。这个函数的形式如callbackfn(data){ data = tmp;success();complete();}。它代理了通过ajax(s)的传入s参数中success();complete()的功能。它就是调用这个函数,实际上是调用success();complete()的函数。

  那么怎么调用呢?ajax不支持跨域。在⑤处,我们可以看到这里是采用scriptTag的方式来完成。先在页面的中添加一个

阅读(1300) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~