详解JavaScript中jQuery和Ajax以及JSONP的联合使用
借助于XMLHttpRequest,浏览器可以在整个页面不刷新的情况下与服务端进行交互,这就是所谓的Ajax(AsynchronousJavaScriptandXML)。Ajax可以为用户提供更为丰富的用户体验。
Ajax请求由JavaScript驱动,通过JavaScript代码向URL发送一个请求,待服务端有响应时会触发一个回调函数,可以在这里回调函数里面处理服务端返回的信息。由于整个发送请求和响应的过程是异步的,所以在此期间页面中其它Javascript代码仍然继续执行,不会中断。
jQuery对Ajax当然也提供了很好的支持,而且还抽象了各种浏览器对于Ajax支持方面另人痛苦的差异。它不但提供了全功能的$.ajax()方法,还有诸如$.get(),$.getScript(),$.getJSON(),$.post()和$().load()等更为简便的方法。
尽管被命名为Ajax,但是很多Ajax应用并没有使用XML,特别是jQuery方面的Ajax应用,大多数都没有使用XML;反而用得比较多的情况是:纯文本、HTML以及JSON(JavaScriptObjectNotation)。
一般情况下,由于同源策略(同协议,同域名,同端口)的限制,Ajax并不能跨域执行请求,除非使用诸如JSONP(JSONwithPadding)之类的方案,才能实现一些受限的跨域功能。
关于Ajax的一些重要概念
GETvsPOST,这是两种最常用的向服务端发送请求的方法,正确理解这两种方法的区别对于Ajax开发非常重要。
GET方法通常用于执行一些非破坏性的操作(就是说,只从服务端获取信息,不修改服务端上的信息)。例如,搜索查询服务一般会使用GET请求。另外,GET请求还可能会被浏览器缓存,这可能会导致一些不可预知的问题。一般情况下GET请求只能通过查询字符串的方式向服务端发送数据。
POST方法通常用于在服务端上执行一些破坏性的操作(就是说,会修改服务端上的数据)。例如,当你发表一篇博客的时候,用的应该就是POST请求。和GET请求不一样,POST请求不存在缓存问题。POST请求中,查询字符串作为URL的一部分也能向服务端提交数据,但不推荐这种方法,所有数据应该跟URL分开单独发送。
数据类型,jQuery通常要求指明服务端返回的数据类型,某些情况写数据类型可能已经包含在方法名称中了,如$.getJSON(),除此之外,它都会被作为一个可配置的对象的一部分,该对象最终会作为$.ajax()方法的参数。数据类型通常有以下几种:
- text:纯文本,用于传输简单的字符串。
- html:用于传输一段HTML。
- script:向页面中添加脚本。
- json:传输已格式化的JSON对象,它可以包含字符串、数组或对象。
- jsonp:用于传输从其他域下返回的JSON数据。
- xml:用于传输自定义的XML格式数据。
异步执行,Ajax中的A指的是异步(Asynchronous)。说到这里可能很多jQuery初学者一下子很难理解什么叫异步,因为默认情况下Ajax请求就是异步的,服务端返回的信息并非马上就能获取到。所有服务端返回的信息只能在一个回调函数中处理。例如以下这段代码,是错误的:
varresponse; $.get('foo.php',function(r){response=r;}); console.log(response);//undefined!
正确的做法应该是在回调函数中处理服务端返回的数据,回调函数在Ajax请求成功完成时才被执行,这个时候才能获取到来自服务端的数据:
$.get('foo.php',function(response){console.log(response);});
同源策略及JSONP,前面已经说过,一般情况下Ajax的请求会被限制在相同协议(http或https)、相同端口、相同域名下才能正确执行,但是HTML的<script>标签却无此限制,它可以载入任何域下的脚本,jQuery正是利用了这一点才得以拥有跨域执行Ajax的能力。
所谓JSONP,就是其它域的服务端返回给我们的是JavaScript代码,这段代码可以被加载到HTML中的<script>标签中,这段JavaScript代码中包含有从其它域下的服务端返回的JSON数据,并以回调函数的形式提供。这样一来jQuery就回避了同源策略的限制,曲线拥有了跨域执行Ajax的能力。
Ajax调试工具,现在比较新的浏览器如Chrome和Safari,甚至IE都内置了调试工具,Firefox也有无比强大FireBug插件,借助于这些调试工具,可以非常详细的查看Ajax的执行过程。
和Ajax相关的一些方法
jQuery提供了好多种简便的Ajax方法,但是它们的核心都是$.ajax,所以必须正确理解$.ajax。
jQuery的$.ajax是创建Ajax请求中最直接也是最有效的方法,它的参数是一个JavaScript对象,我们可以在这个对象中对Ajax作非常详细的配置。另外,$.ajax方法还可以分别定义Ajax请求成功和失败时的回调函数;而且它以一个可配置的对象作为参数的特性,使得我们可以在Ajax方法外配置这个对象,然后再传进来,这非常有助于实现代码复用。关于这个方法的详细文档:http://api.jquery.com/jQuery.ajax/
一个典型的示例如下:
$.ajax({ //要请求的URL url:'post.php', //要发给服务端的数据 //(将会转化为查询字符串,如:?id=123) data:{id:123}, //定义此Ajax请求是POST还是GET type:'GET', //服务端返回的数据类型 dataType:'json', //Ajax成功执行时的回调函数; //回调函数的参数即为服务端返回的数据 success:function(json){ $('<h1/>').text(json.title).appendTo('body'); $('<divclass="content"/>') .html(json.html).appendTo('body'); }, //如果Ajax执行失败; //将返回原始错误信息以及状态码 //传入这个回调函数中 error:function(xhr,status){ alert('Sorry,therewasaproblem!'); }, //这里是无论Ajax是否成功执行都会触发的回调函数 complete:function(xhr,status){ alert('Therequestiscomplete!'); } });
备注:
关于dataType:如果这里定义的dataType跟服务端返回的数据格式不一样,我们的代码就可能会执行失败,并且很难查出原因,因为HTTP返回的状态码并没有显示出错。所以在执行Ajax请求的时候,一定要确保服务端返回的数据格式跟事先定义定义的一致。通常情况下验证HTTP头信息中的Content-type是行之有效的办法,对于JSON而言,对应的Content-type应该是application/json。
$.ajax的一些自定义选项
$.ajax方法的自定义选项非常多,这也是此方法功能强大的原因所在。若要查阅所有自定义选项,可参考官方文档:http://api.jquery.com/jQuery.ajax/,下面只列出一些常用的选项:
async:默认值是true,如果需要Ajax的执行方式为同步,可将其设为false。请注意,如果把这个值设为false了,那么你的其它JavaScript代码将会被中断执行,直到此次Ajax请求完毕,接受到服务端返回的数据为止才会恢复。所以,请慎用此选项。
cache:设定是否缓存服务端发回的数据。对于“script”和“jsonp”之外的其它格式的数据而言,默认值是true。如果被设置为false,向服务器发送请求的时候,URL中会被加入一个查询字符串,字符串的值是当前的时间戳,以确保每次请求的URL都是不同的,当然也就不存在缓存问题了。JavaScript中获取时间戳的方法为newDate().getTime()。
complete:Ajax请求执行完成时触发的回调函数,无论此次执行成功与否,该回调函数都会被触发。该回调函数可以接受服务端返回的原始信息及状态码。
context:定义回调函数执行时的作用域(回调函数function(s){alert(this)}中的this指向谁?)。默认情况下,回调函数中的this指向传递给$.ajax方法的参数,也就是那个大对象。
data:要发送给服务端的数据,其值可以是一个对象或者查询字符串,如foo=bar&baz=bim。
dataType:服务端返回数据的类型。如果不设置这个选项,jQuery会根据服务端返回数据的MIME类型自行判断。
error:当Ajax执行错误时将会触发的回调函数,该函数接受原始的请求信息及状态码。
jsonp:执行JSONP请求时需要制定的回调函数名称,默认值是“callback”。
success:Ajax成功执行时将会触发的回调函数。在函数中可获取服务端返回的信息(如果dataType被设置成JSON,返回的数据应该是一个JavaScript对象),当然也可获取服务端返回的原始数据信息及状态码。
timeout:给Ajax请求设置一个超时是时间,单位是毫秒。
type:指定请求的方式,GET或者POST,默认值是GET。其它如PUT和DELETE方式也可以用,但并不是所有浏览器都支持。
url:要请求的URL。
其中url选项是所有选项中唯一的一个必选项,其它选项都是可选的。
一些简便方法
如果你不需要那么多可配置的选项,也不关心Ajax执行错误时候的相关处理,jQuery同样提供了一些非常有用的简便方法,以更简洁的方式完成Ajax请求。其实这些简便写法只是封装了$.ajax,并把某些选项预先设定好。
jQuery提供的简便方法如下:
- $.get:对给定的URL执行GET请求。
- $.post:对给定的URL执行POST请求。
- $.getScript:向页面中添加脚本。
- $.getJSON:执行一个GET请求,服务端返回的信息应为JSON。
以上每种简便方法中都可传递如下参数:
url:所请求的URL,必须提供。
data:向服务端发送的数据,可选。可以是一个对象,亦或查询字符串,如foo=bar&baz=bim。
- 备注:此选项不适用于$.getScript。
一个回调函数:此回调函数在请求成功执行后被触发。可选。该回调函数接受服务端返回的数据,也包括请求的状态码及原始对象。
数据类型:服务端返回数据的类型。可选。
- 备注:此选项只适用于那些在其名称中没指定数据类型的方法。
下面是三个简便方法的示例:
//获取纯文本或者html $.get('/users.php',{userId:1234},function(resp){ console.log(resp); }); //向页面中添加脚本,然后执行脚本中定义的函数。 $.getScript('/static/js/myScript.js',function(){ functionFromMyScript(); }); //从服务端获取JSON格式的数据。 $.getJSON('/details.php',function(resp){ $.each(resp,function(k,v){ console.log(k+':'+v); }); });
$.fn.load
$.fn.load方法是jQuery所有Ajax方法中唯一在选择器结果集上调用的方法。$.fn.load方法从给定的URL上获取信息,然后填充到选择器结果集包含的元素中。另外,在URL后面还可以附加一些选择器,jQuery最终只会把跟选择器相匹配的内容填充到对应的HTML元素中。
下面是示例:
$('#newContent').load('/foo.html'); //或 $('#newContent').load('/foo.html#myDivh1:first',function(html){ alert('加载完毕!'); });
Ajax和表单
在跟表单打交道方面,jQuery的Ajax更显神威。最为有用的两个方法就是$.fn.serialize和$.fn.serializeArray,下面是它们的用法:
//将表单中数据转化为查询字符串 $('#myForm').serialize(); $('#myForm').serializeArray(); //将表单中数据转化为对象数组,如: [ {name:'field1',value:123}, {name:'field2',value:'helloworld'} ]
使用JSONP
JSON的本质其实是一种跨站点脚本注入。现在有很多比较好的网站都提供了JSONP服务,允许我们用他们预先定义好的API获取他们的数据。下面是一个示例,来源于http://www.giantflyingsaucer.com/blog/?p=2682
服务端代码:
<?php header("content-type:text/javascript"); if(isset($_GET['name'])&&isset($_GET['callback'])){ $obj->name=$_GET['name']; $obj->message="Hello".$obj->name; echo$_GET['callback'].'('.json_encode($obj).');'; } ?>
客户端代码:
$.ajax({ url:'http://otherDomail.com:8080/JSONP/jsonp-demo.php', data:{name:'Superman'}, dataType:'jsonp', jsonp:'callback', success:function(response){ console.log(response.message); } });
jQuery把JSONP的实现细节隐藏在幕后,我们要做的就是告诉jQuery服务端定义好的函数名以及我们请求的数据类型是JSONP,其它方面和普通的Ajax请求没什么区别。
Ajax事件
很多时候我们都需要在Ajax请求开始或结束时做一些操作,例如显示或隐藏一个loading…图片。这些工作本可以在每个Ajax请求中各自实现,但是jQuery提供了更好的方法,你可以像绑定普通事件一样绑定Ajax事件。若要参阅全部事件列表,可访问http://docs.jquery.com/Ajax_Events。下面是简单示例:
$('#loading_indicator') .ajaxStart(function(){$(this).show();}) .ajaxStop(function(){$(this).hide();});