您的位置:首页 > Web前端 > JQuery

jQuery的ajax 简单解析与实现

2011-09-15 16:58 267 查看
.wt0914{
font-size:13px;
}
.wt0914 .bule{
color:#3468A4;
}
.wt0914 p{
line-height:25px;
text-indent:15px;
}
.wt0914 ul li{
list-style-type:none;
}

以下的内容只是个人的理解,技术有限,如有问题请指正(看得jq版本是1.3.2)

jQuery的ajax, 就是我看的和理解,可以用在3个方面,一个是取一个script地址并执行一些操作 $jQuery.getScript; 一个是jsonp $jQuery.getJSON;还有一个就是一般的ajax应用了我主要记录的是jsonp 和 ajax 这方面的个人理解..

jsonp

我的理解是:
创建一个全局的回调函数(假如是handler)
在创建一个<script>标签,去请求一个服务器端的地址(假如是http://www.xxx.com, 写在script标签里面就是 http://www.xxx.com?callback=handler callback是跟别人约定好的,也可以不要callback 别人主要是通过这个约定好的callback取到 函数名 handler 然后返回一个可以执行的js)
服务器端返回一个可以执行的js代码 (假如是 handler({name : "hello world"}))

一个简单的例子<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312" />
<title></title>
</head>
<body>
<input value="faqingqiu" onClick="xx()" type="button">

<script type="text/javascript">
function gg(d){
if(d.toSource){
alert(d.toSource())
}else{
alert(d.name)
}

}
function xx(){
var head = document.getElementsByTagName("head")[0];
var script = document.createElement("script");
script.src = "http://active.zol.com.cn/guofeng/profile2.php?callback=gg";
head.appendChild(script);
}
</script>
</body>
</html>

上面的handler是 gg函数
请求的地址是 http://active.zol.com.cn/guofeng/profile2.php?callback=gg
截图为证
View Code

//extend 的第一个参数为true 表示深度复制
//创建一个新的对象 jq.ajaxSettings全部copy过去 把s复制过去
s = jq.extend(true, s, jq.extend(true, {}, jq.ajaxSettings, s));
var jsonp,
// jsonp 的回调函数名字要以这种方式才行
jsre = /=\?(&|$)/g,
status,
data,
type = s.type.toUpperCase();

//如果data不是字符串 转成字符串
if ( s.data && typeof s.data !== "string" )
s.data = jq.param(s.data);

if ( s.dataType == "jsonp" ) {
if ( type == "GET" ) {
if ( !s.url.match(jsre) ){
//get 方式 参数都在url的后面
//如果没有=?这种形式 就转成这种形式 这是jq的jsonp形式 后面要把?变成函数名
//转成这种形式 http://www.xxx.com?callback=? s.url += (s.url.match(/\?/) ? "&" : "?") + (s.jsonp || "callback") + "=?";
}//http://www.baidu.com?callback=?
} else if ( !s.data || !s.data.match(jsre) ){
//post方式 有专为存放参数的地方 这里是把参数和回调都转一下
//形式 xx=xx&oo=oo.......&callback=?
s.data = (s.data ? s.data + "&" : "") + (s.jsonp || "callback") + "=?";
}
s.dataType = "json";
}

// url 符合 定义的jsonp形式 或者 s.data符合jsonp形式
if ( s.dataType == "json" && (s.data && s.data.match(jsre) || s.url.match(jsre)) ) {
// 这里的jsonp 就是后面的可执行函数的 函数名了
jsonp = "jsonp" + jsc++;

// Replace the =? sequence both in the query string and the data
// /=\?(&|$)/g
//将 ? 转 成函数名字
if ( s.data )
s.data = (s.data + "").replace(jsre, "=" + jsonp + "$1");
s.url = s.url.replace(jsre, "=" + jsonp + "$1");
s.dataType = "script";

// 定义全局的函数名了 后面请求完毕后就会执行这个函数
window[ jsonp ] = function(tmp){
data = tmp;
success();
//complete();
// Garbage collect
window[ jsonp ] = undefined;
try{ delete window[ jsonp ]; } catch(e){}
if ( head )
head.removeChild( script );
};
}

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
//alert(s.url) http://www.baidu.com?callback=jsonp1315980993828 var ret = s.url.replace(/(\?|&)_=.*?(&|$)/, "$1_=" + ts + "$2");

// if nothing was replaced, add timestamp to the end
s.url = ret + ((ret == s.url) ? (s.url.match(/\?/) ? "&" : "?") + "_=" + ts : "");
}*/
// If data is available, append data to url for get requests
if ( s.data && type == "GET" ) {
//加上其他的参数
s.url += (s.url.match(/\?/) ? "&" : "?") + s.data;

// IE likes to send both get and post data, prevent this
//ie的 get post 都会传送s.data 好像就是这个意思 所以节省开支
s.data = null;
}

// Watch for a new set of requests
//if ( s.global && ! jQuery.active++ )
// jQuery.event.trigger( "ajaxStart" );

// Matches an absolute URL, and saves the domain
//取url的 前面的信息 和 域名
var parts = /^(\w+:)?\/\/([^\/?#]+)/.exec( s.url );

if ( s.dataType == "script" && type == "GET" && parts
&& ( parts[1] && parts[1] != location.protocol || parts[2] != location.host )){
var head = document.getElementsByTagName("head")[0];
var script = document.createElement("script");
script.src = s.url;
if (s.scriptCharset)
script.charset = s.scriptCharset;

//如果不是jsonp方式 就是创建一个script标签
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 jq里面可以说反复的强调了 删除元素时 该dom元素引用的对象要断开引用 不然会内存泄露
script.onload = script.onreadystatechange = null;
head.removeChild( script );
}
};
}
head.appendChild(script);
// We handle everything using the script element injection
return undefined;
}

jQuery.ajax 的后面的部分就是处理ajax的了
jq有专门的设置请求头的部分
其中的 If-Modified-Since 比较重要了
可以看看这篇文章
还有这个
jq里面用 jquery.lastModified 来保存最后的修改时间的
形式是 jquery.lastModified[url] = 时间
按我的理解是
我发送一次请求后 服务器看在 If-Modified-Since 里面的时间后有没有做修改,没有做修改返回304 读缓存的文件 有做修改返回200
这么做的好处是可以节约网络资源咯

jq1.3.2里面是设置的定时器 去判断请求是否已经完成
onreadystatechange是定时器去执行的函数
如果设置了s.timeout
会在这个时间后去查看是否已经xhr的状态 是否已经完成了回调 如果没有 abort 终止掉请求

jq还有一些方法配合 ajajx
jquery.httpSuccess
jquery.httpNotModified
jquery.httpData

jquery.httpSuccess
判断请求是否已经成功了
jquery.httpNotModified
判断 服务器那边是否已经做过了修改
jquery.httpData
因为返回的数据是根据请求头的content-type来的,所以要对不同的数据做处理
因为返回的数据是根据请求头的content-type来的,所以要对不同的数据做处理
在这里 又对 xml script json 的处理
其中script 用到了jquery.globalEval

一个完整的例子
<!DOCTYPE html >
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312" />
<title></title>
</head>
<body>
<script type="text/javascript">
(function(undefined){
var win = this,
toString = Object.prototype.toString;
function now(){
return +new Date;
}
var jsc = now();
win.jq = {
isFunction : function(obj){
return toString.call(obj) === "[object Function]";
},
isArray : function(obj){
return toString.call(obj) === "[object Array]";
},
each : function(object, callback, args){
var name, i = 0, length = object.length;
if ( args ) {
if ( length === undefined ) {
for ( name in object )
if ( callback.apply( object[ name ], args ) === false )
break;
} else
for ( ; i < length; )
if ( callback.apply( object[ i++ ], args ) === false )
break;
} else {
if ( length === undefined ) {
for ( name in object )
if ( callback.call( object[ name ], name, object[ name ] ) === false )
break;
} else
for ( var value = object[0];
i < length && callback.call( value, i, value ) !== false; value = object[++i] ){}
}
return object;
},
extend : function(){
var target = arguments[0] || {}, i = 1, length = arguments.length, deep = true, options;
if ( typeof target === "boolean" ) {
deep = target;
target = arguments[1] || {};
i = 2;
}
if ( typeof target !== "object" && Object.prototype.toString.call(target)!="[object Function]")
target = {};
for(;i<length;i++){
if ( (options = arguments[ i ]) != null )
for(var name in options){
var src = target[ name ], copy = options[ name ];
if ( target === copy )
continue;
if ( deep && copy && typeof copy === "object" && !copy.nodeType ){
target[ name ] = arguments.callee( deep, src || ( copy.length != null ? [ ] : { } ), copy );
}
else if(copy !== undefined)
target[ name ] = copy;
}
}
return target;
},
param : function(a){
var s = [];
function add(key,value){
s[s.length] = encodeURIComponent(key) + '=' + encodeURIComponent(value);
};
if(jq.isArray(a)){
jq.each(a,function(){
add(this.name,this.value);
});
}else{
for(var j in a){
if(jq.isArray(a[j]))
jq.each(a,function(){
add(this.name,this.value);
});
else
add( j , jq.isFunction(a[j]) ? a[j]() : a[j] );
}
};
return s.join("&").replace(/%20/g, "+");
},
// Determines if an XMLHttpRequest was successful or not
httpSuccess: function( xhr ) {
try {
// IE error sometimes returns 1223 when it should be 204 so treat it as success, see #1450
return !xhr.status && location.protocol == "file:" ||
( xhr.status >= 200 && xhr.status < 300 ) || xhr.status == 304 || xhr.status == 1223;
} catch(e){}
return false;
},
// Determines if an XMLHttpRequest returns NotModified
httpNotModified: function( xhr, url ) {
//判断 服务器那边是否已经做过了修改
try {
var xhrRes = xhr.getResponseHeader("Last-Modified");
// Firefox always returns 200. check Last-Modified date
// 好像 firefox无视是否修改吗 没测试过
return xhr.status == 304 || xhrRes == jq.lastModified[url];
} catch(e){}
return false;
},
httpData: function( xhr, type, s ) {
var ct = xhr.getResponseHeader("content-type"),
xml = type == "xml" || !type && ct && ct.indexOf("xml") >= 0,
//判断返回的是xml还是字符串
data = xml
? xhr.responseXML
: xhr.responseText;
if ( xml && data.documentElement.tagName == "parsererror" )
throw "parsererror";
// Allow a pre-filtering function to sanitize the response
// s != null is checked to keep backwards compatibility
//if( s && s.dataFilter )
// data = s.dataFilter( data, type );
// The filter can actually parse the response
if( typeof data === "string" ){
// If the type is "script", eval it in global context
//如果类型是script 则创建一个标签 并且执行data里面的内容
if ( type == "script" )
jq.globalEval( data );
// Get the JavaScript object, if JSON is used.
if ( type == "json" )
data = window["eval"]("(" + data + ")");
}
return data;
},
handleError : function(){
},
// Evalulates a script in a global context
globalEval: function( data ) {
if ( data && /\S/.test(data) ) {
// Inspired by code by Andrea Giammarchi
// http://webreflection.blogspot.com/2007/08/global-scope-evaluation-and-dom.html var head = document.getElementsByTagName("head")[0] || document.documentElement,
script = document.createElement("script");
script.type = "text/javascript";
//if ( jQuery.support.scriptEval )
//script.appendChild( document.createTextNode( data ) );
//else
script.text = data;
// Use insertBefore instead of appendChild to circumvent an IE6 bug.
// This arises when a base node is used (#2709).
head.insertBefore( script, head.firstChild );
head.removeChild( script );
}
},
get: function( url, data, callback, type ) {
// shift arguments if data argument was ommited
if ( jq.isFunction( data ) ) {
callback = data;
data = null;
}
return jq.ajax({
type : "GET",
url : url,
data : data,
success : callback,
dataType : type
});
},
getScript: function( url, callback ) {
return jq.get(url, null, callback, "script");
},
getJSON: function( url, data, callback ) {
return jq.get(url, data, callback, "json");
},
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,
*/
// Create the request object; Microsoft failed to properly
// implement the XMLHttpRequest in IE7, so we use the ActiveXObject when it is available
// This function can be overriden by calling jQuery.ajaxSetup
xhr:function(){
return window.ActiveXObject
? new ActiveXObject("Microsoft.XMLHTTP")
: new XMLHttpRequest();
},
accepts: {
xml: "application/xml, text/xml",
html: "text/html",
script: "text/javascript, application/javascript",
json: "application/json, text/javascript",
text: "text/plain",
_default: "*/*"
}
},
lastModified : {},
ajax : function(s){
//extend 的第一个参数为true 表示深度复制
//创建一个新的对象 jq.ajaxSettings全部copy过去 把s复制过去
s = jq.extend(true, s, jq.extend(true, {}, jq.ajaxSettings, s));
var jsonp,
// jsonp 的回调函数名字要以这种方式才行
jsre = /=\?(&|$)/g,
status,
data,
type = s.type.toUpperCase();
//如果data不是字符串 转成字符串
if ( s.data && s.processData&&typeof s.data !== "string" )
s.data = jq.param(s.data);
if ( s.dataType == "jsonp" ) {
if ( type == "GET" ) {
if ( !s.url.match(jsre) ){
//get 方式 参数都在url的后面
//如果没有=?这种形式 就转成这种形式 这是jq的jsonp形式 后面要把?变成函数名
//转成这种形式 http://www.xxx.com?callback=? s.url += (s.url.match(/\?/) ? "&" : "?") + (s.jsonp || "callback") + "=?";
}//http://www.baidu.com?callback=?
} else if ( !s.data || !s.data.match(jsre) ){
//post方式 有专为存放参数的地方 这里是把参数和回调都转一下
//形式 xx=xx&oo=oo.......&callback=?
s.data = (s.data ? s.data + "&" : "") + (s.jsonp || "callback") + "=?";
}
s.dataType = "json";
}
// url 符合 定义的jsonp形式 或者 s.data符合jsonp形式
if ( s.dataType == "json" && (s.data && s.data.match(jsre) || s.url.match(jsre)) ) {
// 这里的jsonp 就是后面的可执行函数的 函数名了
jsonp = "jsonp" + jsc++;
// Replace the =? sequence both in the query string and the data
// /=\?(&|$)/g
//将 ? 转 成函数名字
if ( s.data )
s.data = (s.data + "").replace(jsre, "=" + jsonp + "$1");
s.url = s.url.replace(jsre, "=" + jsonp + "$1");
s.dataType = "script";
// 定义全局的函数名了 后面请求完毕后就会执行这个函数
window[ jsonp ] = function(tmp){
data = tmp;
success();
//complete();
// Garbage collect
window[ jsonp ] = undefined;
try{ delete window[ jsonp ]; } catch(e){}
if ( head )
head.removeChild( script );
};
}
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
//alert(s.url) http://www.baidu.com?callback=jsonp1315980993828 var ret = s.url.replace(/(\?|&)_=.*?(&|$)/, "$1_=" + ts + "$2");
// if nothing was replaced, add timestamp to the end
s.url = ret + ((ret == s.url) ? (s.url.match(/\?/) ? "&" : "?") + "_=" + ts : "");
}*/
// If data is available, append data to url for get requests
if ( s.data && type == "GET" ) {
//加上其他的参数
s.url += (s.url.match(/\?/) ? "&" : "?") + s.data;
// IE likes to send both get and post data, prevent this
//ie的 get post 都会传送s.data 好像就是这个意思 所以节省开支
s.data = null;
}
// Watch for a new set of requests
//if ( s.global && ! jQuery.active++ )
// jQuery.event.trigger( "ajaxStart" );
// Matches an absolute URL, and saves the domain
//取url的 前面的信息 和 域名
var parts = /^(\w+:)?\/\/([^\/?#]+)/.exec( s.url );
if ( s.dataType == "script" && type == "GET" && parts
&& ( parts[1] && parts[1] != location.protocol || parts[2] != location.host )){
var head = document.getElementsByTagName("head")[0];
var script = document.createElement("script");
script.src = s.url;
if (s.scriptCharset)
script.charset = s.scriptCharset;
//如果不是jsonp方式 就是创建一个script标签
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 jq里面可以说反复的强调了 删除元素时 该dom元素引用的对象要断开引用 不然会内存泄露
script.onload = script.onreadystatechange = null;
head.removeChild( script );
}
};
}
head.appendChild(script);
// We handle everything using the script element injection
return undefined;
}
//用一个保证回掉函数只执行一次
var requestDone = false;
var xhr = s.xhr();
// Open the socket
// Passing null username, generates a login popup on Opera (#2865)
if( s.username )
xhr.open(type, s.url, s.async, s.username, s.password);
else
xhr.open(type, s.url, s.async);
try {
if ( s.data )
xhr.setRequestHeader("Content-Type", s.contentType);
// Set the If-Modified-Since header, if ifModified mode.
//If-Modified-Since 记录页面最后修改时间的 HTTP 头信息
// 会和服务器的时间比较 在这个时间内 返回的是304
if ( s.ifModified )
xhr.setRequestHeader("If-Modified-Since",
jq.lastModified[s.url] || "Thu, 01 Jan 1970 00:00:00 GMT" );
// Set header so the called script knows that it's an XMLHttpRequest
xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
// Set the Accepts header for the server, depending on the dataType
xhr.setRequestHeader("Accept", s.dataType && s.accepts[ s.dataType ] ?
s.accepts[ s.dataType ] + ", */*" :
s.accepts._default );
} catch(e){}
//发送钱的回调 如果回调函数的返回是false abort掉
if ( s.beforeSend && s.beforeSend(xhr, s) === false ) {
xhr.abort();
return false;
}
var onreadystatechange = function(isTimeout){
// The request was aborted, clear the interval and decrement jQuery.active
if (xhr.readyState == 0) {
if (ival) {
// clear poll interval
clearInterval(ival);
ival = null;
}
// The transfer is complete and the data is available, or the request timed out
} else if ( !requestDone && xhr && (xhr.readyState == 4 || isTimeout == "timeout") ) {
//保证回调只执行一次
requestDone = true;
//清空掉定时器
if (ival) {
clearInterval(ival);
ival = null;
}
status = isTimeout == "timeout"
? "timeout"
: !jq.httpSuccess( xhr )
? "error"
: s.ifModified && jq.httpNotModified( xhr, s.url )
? "notmodified"
: "success";
//判断是否已经超时
if ( status == "success" ) {
// Watch for, and catch, XML document parse errors
try {
// process the data (runs the xml through httpData regardless of callback)
data = jq.httpData( xhr, s.dataType, s );
} catch(e) {
status = "parsererror";
}
}
// Make sure that the request was successful or notmodified
if ( status == "success" ) {
// Cache Last-Modified header, if ifModified mode.
var modRes;
try {
modRes = xhr.getResponseHeader("Last-Modified");
} catch(e) {} // swallow exception thrown by FF if header is not available
//记录下最后请求的时间 因为之后再请求的话 还要继续和服务器的时间比较
if ( s.ifModified && modRes )
jq.lastModified[s.url] = modRes;
// JSONP handles its own success callback
if ( !jsonp )
success();
} else
jq.handleError(s, xhr, status);
// Fire the complete handlers
complete();
if ( isTimeout )
xhr.abort();
// Stop memory leaks
if ( s.async )
xhr = null;
}
};
if ( s.async ) {
// don't attach the handler to the request, just poll it instead
var ival = setInterval(onreadystatechange, 13);
// Timeout checker
if ( s.timeout > 0 )
setTimeout(function(){
// Check to see if the request is still happening
if ( xhr && !requestDone )
onreadystatechange( "timeout" );
}, s.timeout);
}
xhr.send(s.data);
function success(){
// If a local callback was specified, fire it and pass it the data
if ( s.success )
s.success( data, status );
}
function complete(){
// Process result
if ( s.complete )
s.complete(xhr, status);
}
},
xx :'1'
}
})();
window.onload = function(){
}
</script>
</body>
<input value="jsonp" type="button" onClick="d1()"><br/><br/><br/>
<input value="一般的请求" type="button" onClick="d2()"><br/><br/><br/>
本想弄个If-Modified-Sinc的例子 但是发现cnblogs的xhr的请求的响应头 全部都没有Last-Modifie
<script type="text/javascript">
function t(data){
'toSource' in data
? alert(data.toSource())
: alert(data.name)
}
function d1(){
jq.getJSON("http://active.zol.com.cn/guofeng/profile2.php?callback=?", t);
}
function d2(){
jq.ajax({
type : 'post',
ifModified : true,
dataType : 'json',
processData : false,
contentType : 'application/json',
data : '{"postId":2169079,"blogId":56660,"dateCreated":"2011/9/6 18:54:00"}',
url : "http://www.cnblogs.com/ws/BlogDetailWs.asmx/GetPrevNext",
success : function(data){
if('toSource' in data){
alert(data.toSource())
}else{
for(var name in data){
alert(name+':'+data[name])
}
}
}
});
}
function d3(){
jq.ajax({
type : 'post',
ifModified : true,
dataType : 'json',
ifModified : true,
processData : false,
contentType : 'application/json',
data : '{"postId":2169079,"blogId":56660,"dateCreated":"2011/9/6 18:54:00"}',
url : "http://www.cnblogs.com/ws/BlogDetailWs.asmx/GetPrevNext",
success : function(data){
if('toSource' in data){
alert(data.toSource())
}else{
for(var name in data){
alert(name+':'+data[name])
}
}
}
});
}
</script>
</html>
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: