博客
电影
宝箱
友链
关于
<
CSS变形matrix与动画cubic-bezier
JavaScript类的语法糖
>
原生ajax及jQuery封装ajax实例
作者:
Cifer
类别: 技术·JS
时间:2016-10-23 00:25:19
字数:15139
版权所有,未经允许,请勿转载,谢谢合作~
#### 前言 ajax全称“Asynchronous Javascript And XML”(异步JavaScript和XML),最常用的场景就是:异步加载数据从而实现局部刷新页面。 虽然现在来看,是个老掉牙的东西,在2005年那会,可是个时髦的玩意,可谓占尽了风头!这得简单了解互联网网页的历史,网页发展,主要有4个革命性阶段: 1.静态黄页阶段 早期网站页面一般都是静态页,页面的所有内容都固定的,主要代表是引流的导航网站以及最初企业展示网站。 2.动态页面阶段 以jsp,asp,php等动态页面,在服务器中使用不同的数据动态生成不同的html,弥补了静态页不灵活的特点。 3.ajax阶段 如果需要更新页面,无论刷新整个页面,只需与服务进行少量所需的数据,局部刷新页面,减少代宽占用,提高了用户体验。 4.websocket阶段 html5的双向通信的持久化协议,解决服务器无法主动向客服端发起请求的问题,支持当客服端发起第一个request请求后(websocket与http不是一个协议,只是兼容http而已),服务器给其建立一个"长连接“,有新数据时,主动向客服端发起请求,并且之后无需再发起request请求。 #### jQuery的ajax 如果说ajax在实际项目最常见,那一定是jQuery封装下的用法。 写ajax请求之前,[西法](http://www.boatsky.com "太空船博客")先写个简单的demo接口,本处以php为例(使用的是laraval框架),分别有get与post的方法: ```php function get(Request $request) { $name = $request->input('name'); $age = $request->input('age'); $t = time();//返回时间以区别重复的请求 if (!empty($name) && !empty($age)) { $resp = array( 'errcode' => 0, 'errmsg' => '请求成功', 'args' => array( 'type' => 'get_interface', 'name' => $name, 'age' => $age, 't' => $t ) ); } else { $resp = array( 'errcode' => 404, 'errmsg' => '无法获取name或age值' ); } return response()->json($resp); } function post(Request $request) { $name = $request->input('name'); $age = $request->input('age'); $t = time(); if (!empty($name) && !empty($age)) { $resp = array( 'errcode' => 0, 'errmsg' => '请求成功', 'args' => array( 'type' => 'post_interface', 'name' => $name, 'age' => $age, 't' => $t ) ); } else { $resp = array( 'errcode' => 404, 'errmsg' => '无法获取name或age值' ); } return response()->json($resp); } ``` 可以看到,上面并不只是返回数据,同时返回code与msg,为什么要多此一举?这就是为了规范,code是结果代码,msg是返回结果信息。我曾经见到人有把code值设成success,error这样的单词,其实很不好,不便于统一管理,code一般是返回10进制数字或16进制数字(这种更有优势,因为16进制很方便的转成2进制,每一个位,都是可以写成一个开关,结果则是不同的开关组合),这些数字都是来自统一的配置文件,想必大家用过不同的软件,如果软件报错,一般 都会返回类似的数字。 ##### $.get() 方法 ```javascript $.get( 'http://www.boatsky.com/demo/ajax/get', { name : 'boatsky', age : 2 }, function(response,status){ var responseObj = JSON.parse(response); if(responseObj.code == 0){ console.log(responseObj.data); } else { console.log(response.msg); } }, 'text'//默认值text。有"xml","html","text","script","json","jsonp" ); ``` 如果请求是text,即使后台已经转成json,则返回数据在js中依然需要再转换成对象 ##### $.post() 方法 ```javascript $.post( 'http://www.boatsky.com/demo/ajax/post', { name : 'boatsky', age : 2 }, function(response,status){ if(response.code == 0){ console.log(response.data); } else { console.log(response.msg); } }, 'json' ); ``` $.post()方法与$.get()方法是一样的,区别是$.get()把参数放在url上,而$.post同样是把参数转成get的形式,却使用data去传值。 这里有一个传参与返回结果打印的例子 (略,不可用,因为网站开启了token验证) 我们可以看看区别传参区别: url:http://www.boatsky.com/demo/ajax/get?name=ingdan&age=3 type:GET async:true jsonp:callback dataType:text t:1474791319117 url:http://www.boatsky.com/demo/ajax/get type:POST async:true jsonp:callback dataType:json data:name=ingdan&age=3 t:1474791569375 因为请求时是json,所以返回结果无需再转成对象 ##### $.getJSON() 方法 $.getJSON()方法是在$.get()方法之上封装的,只是默认加入JSON数据请求: ```javascript $.getJSON( 'http://www.boatsky.com/demo/ajax/get', { name : 'boatsky', age : 2 }, function(response,status){ if(response.code == 0){ console.log(response.data); } else { console.log(response.msg); } } ); ``` 有趣的是jQuery,jQuery并没有$.postJSON()方法,但你可以基于$.get()方法写一个,不过,完全没有必要,因为以上的三种方法,都不是很推荐使用。 #### $.ajax()方法 上面也提过$.get(),$.post()方法都是在$.ajax()方法上封装的,所谓的封装,就是要多执行一些东西,本着节约的原则,能用更底层,就用底层的方法,毕竟这些方法使用都非常简单,我宁可选择速度更快的那一个。除了更底层,$.ajax的方法扩展性也更好,可以更方便的添加一些参数。 ```javascript $.ajax({ type : 'get', dataType: 'json', url : 'http://www.boatsky.com/demo/ajax/get', async : true,//默认值可不填 data : { name : 'boatsky', age : 2 }, error : function(er){ console.log(er); }, success : function(response){ if(response.code == 0){ console.log(response.data); } else { console.log(response.msg); } } }); ``` $.ajax()方法功能很强大!除上面这个例子的参数,还可以传cache,beforeSend,complete,dataFilter……众多参数,官网上都有,就不一一列举了。 #### 原生ajax实现 这里我写了一段简单的原生js,可以传值'get','post'来实现两种ajax方法。 ```javascript //原生js实现ajax方法 function doAjax(reqParam){ var xmlHttp; if (window.XMLHttpRequest) { xmlHttp = new XMLHttpRequest(); } else if (window.ActiveXObject) { xmlHttp = new ActiveXObject("Microsoft.XMLHTTP"); } var type = reqParam.type; if(type){ //获得&型的参数 if(reqParam.data){ var tempParam = ''; for(var key in reqParam.data){ if(tempParam){ tempParam = tempParam + '&'+ key + '=' + reqParam.data[key]; } else { tempParam = key + '=' + reqParam.data[key]; } } reqParam.dataParam = tempParam; } if(type.toLowerCase() == 'post'){ xmlHttp.open("POST",reqParam.url,true); xmlHttp.setRequestHeader("Content-Type","application/x-www-form-urlencoded"); xmlHttp.send(reqParam.dataParam); } else if(type.toLowerCase() == 'get'){ xmlHttp.open("GET",reqParam.url + '?' + reqParam.dataParam,true); xmlHttp.setRequestHeader("Content-Type","application/x-www-form-urlencoded"); xmlHttp.send(null); } } xmlHttp.onreadystatechange = function() { //0,Uninitialized,初始化状态。XMLHttpRequest 对象已创建或已被 abort() 方法重置。 //1,Open,open() 方法已调用,但是 send() 方法未调用。请求还没有被发送。 //2,Sent,Send() 方法已调用,HTTP 请求已发送到 Web 服务器。未接收到响应。 //3,Receiving,所有响应头部都已经接收到。响应体开始接收但未完成。 //4,Loaded,HTTP 响应已经完全接收。 if(xmlHttp.readyState == 4){ if(xmlHttp.status == 200){ var responseObj = JSON.parse(xmlHttp.responseText); if(responseObj.code == 0){ console.log(responseObj.data); } else { console.log(responseObj.msg); } } else { console.log(xmlHttp); } } }; } var reqGetParam = { type : 'get', url : 'http://www.boatsky.com/demo/ajax/get', data : { name : 'boatsky', age : 2, t : new Date().getTime() } }; var reqPostParam = { type : 'post', url : 'http://www.boatsky.com/demo/ajax/post', data : { name : 'boatsky', age : 2, t : new Date().getTime() } }; doAjax(reqGetParam); doAjax(reqPostParam); ``` 看到doAjax,是不是有一点点$.ajax()的影子,又回到原地的感觉?没错jQuery实现的ajax方法比doAjax强大的多,如此好用,我们为什么还要用原生的方法? #### jsonp跨域 以上的方式都只能实现网站内容的通信,对于不同网站之间的数据交换则无能为力,随着网站的复杂,跨域是不得不面对的问题,而json(JSON with Padding)是最初的解决方案。其实$.get(),$.getJSON(),$.ajax()都实现了jsonp的方法,之所以单独提出来讲,因为其场景特殊且实现方式不太一样。 jsonp的实现对方法有要求,即需要用一个方法把返回数据包裹起来,下面我们展示一个后台的jsonp接口: ```php function jsonp(Request $request) { $callback = $request->input('callback'); $name = $request->input('name'); $age = $request->input('age'); $t = time(); if (!empty($name) && !empty($age)) { $resp = array( 'errcode' => 0, 'errmsg' => '请求成功', 'args' => array( 'type' => 'jsonp_interface', 'name' => $name, 'age' => $age, 't' => $t ) ); } else { $resp = array( 'errcode' => 404, 'errmsg' => '无法获取name或age值' ); } return response($callback . '(' . json_encode($resp) . ')'); } ``` 再展示一个$.ajax()形式的jsonp请求例子: ```javascript $.ajax({ type : 'get', dataType: 'jsonp', jsonp : 'callback', jsonpCallback : "jsonpCallbackFunction", url : 'http://www.yrczone.com/ajax/jsonp', data : { name : 'boatsky', age : 2, t : new Date().getTime() }, error : function(er){ console.log(er); }, success : function(response){ if(response.code == 0){ console.log(response.data); } else { console.log(response.msg); } } }); ``` 你能看到这里有三个参数不同,dateType:'jsonp'表示数据类型,jsonp:‘callback'为回调函数参数名,默认callback,刚好与后台的参数$callback一致,而jsonpCallback:'jsonpCallbackFunction'则表示回调函数名,如果不填,默认是一个随机函数类似callback=jQuery3100557689627778365_1474459471916的回调。执行完jsonpCallbackFunction函数后,jquery会再把它删除,你会有那么一瞬间看到插入的话脚本函数。为了看到那么一瞬间,也为了知道jsonp原理,我们再看看js是怎么实现jsonp的: ```javascript //获得jsonp数据 function getJsonp(){ var data = { name : 'boatsky', age : 2, t : new Date().getTime() }; var dataParam = ''; if(data){ for(var key in data){ if(dataParam){ dataParam = dataParam + '&'+ key + '=' + data[key]; } else { dataParam = key + '=' + data[key]; } } } var reqParam = {}; reqParam.url = 'http://www.yrczone.com/ajax/jsonp?callback=jsonpCallbackFunction'; if(dataParam){ reqParam.url = reqParam.url + '&' + dataParam; } makeScript(reqParam); } //创建script function makeScript(reqParam){ var script = document.getElementById('jsonpScript'); var head = document.getElementsByTagName('head')[0]; if(script){ head.removeChild(script); } script = document.createElement('script'); script.setAttribute('id','jsonpScript'); script.src = reqParam.url; document.getElementsByTagName('head')[0].appendChild(script); } //回调函数 function jsonpCallbackFunction(response){ if(response.code == 0){ console.log(response.data); } else { console.log(response.msg); } } getJsonp(); ``` 从js可以看到jsonp的实现方式有点特别,只是从服务器拿一个“函数”(只是这个函数里面包括了数据,本例中是jsonpCallbackFunction({"code":0,"msg":"\u8bf7\u6c42\u6210\u529f","data":{"type":"jsonp_interface","name":"boatsky","age":"2","t":1474808753}})),然后,再把该函数插入至script标签中执行。同时可以看到jsonp只能是get的方法,没有post方法,因此传递数据有限制。有的浏览器与服务器对url长度限制,比如有个1024字节的传说,其实绝大多数浏览器与服务器的限制都比这个大的多,只是大家习惯于最早期1024了,因为如果数据量真的大,就应该使用post方法,但post如何实现跨域呢? #### cors跨域 cors,cors,cors,重要的事说三遍! cors,Cross-Origin Resource Sharing,解决了jsonp无法使用post的问题,更重要的是,安全!试想一下,如果jsonp从服务器拿下的数据是一个未知的脚本,然后你还拿着这段在你的域名下运行!而cors则会对域名进行验证,我们不再像jsonp一样用函数包裹,而是像普通的ajax一样使用,只是我们需要在方法里允许cors: ```php function cors(Request $request){ //容许跨域的结合 $allowOrigin = array( 'http://www.boatsky.com' => '/^http(s?):\/\/www\.boatsky\.com(\/|$)/', 'http://www.yrczone.com' => '/^http(s?):\/\/www\.yrczone\.com(\/|$)/', ); $referer = $_SERVER['HTTP_REFERER']; $originFlag = false; $thisHost = $_SERVER['HTTP_HOST']; foreach($allowOrigin as $key => $origin){ if(preg_match($origin,$referer)){ $originFlag = true; $thisHost = $key; break; } } //如果匹配以上,才容许cors if($originFlag){ header('Access-Control-Allow-Origin: '.$thisHost); } $name = $request->input('name'); $age = $request->input('age'); $t = time(); if(!empty($name) && !empty($age)){ $resp = array( 'errcode' => 0, 'errmsg' => '请求成功', 'args' => array( 'type' => 'cors_interface', 'name' => $name, 'age' => $age, 'host' => $_SERVER['HTTP_HOST'], 'referer' => $referer, 't' => $t ) ); } else { $resp = array( 'errcode' => 404, 'errmsg' => '无法获取name或age值' ); } return response()->json($resp); } ``` 其实[cifer](http://www.boatsky.com "太空船博客")做了两个域名的匹配,只有这两个域名 www.boatsky.com 及www.yrczone.com 才允许访问cors方法的返回数据。如果想允许所有域名,则只需要写上*号即可,这就是在方法里限定域名的方法,还有一种是在服务器上限定,比如nginx的配置 ``` http { ...... add_header Access-Control-Allow-Origin *; add_header Access-Control-Allow-Headers X-Requested-With; add_header Access-Control-Allow-Methods GET,POST,OPTIONS; ...... } ``` 以下则是对get与post进行封装的cors代码: ```javascript //cors get例子 function getCorsData(){ var corsUrl = 'http://www.yrczone.com/ajax/cors'; var data = { name : 'boatsky', age : 2, t : new Date().getTime() }; var dataParam = dataToGetParam(data); var thisCorsUrl = ''; if(dataParam){ thisCorsUrl = corsUrl + '?' + dataParam; } var reqGetParam = { type : 'get', url : thisCorsUrl }; corsData(reqGetParam); } getCorsData(); //cors post例子 function postCorsData(){ var corsUrl = 'http://www.yrczone.com/ajax/cors'; var data = { name : 'boatsky', age : 2, t : new Date().getTime() }; var dataParam = dataToGetParam(data); var reqGetParam = { type : 'post', url : corsUrl, dataParam : dataParam }; corsData(reqGetParam); } postCorsData(); //cors处理 function corsData(reqParam){ var xmlHttp; if (window.XMLHttpRequest) { xmlHttp = new XMLHttpRequest(); } else if (window.ActiveXObject) { xmlHttp = new ActiveXObject("Microsoft.XMLHTTP"); } if ("withCredentials" in xmlHttp) { // 检查XMLHttpRequest对象是否有“withCredentials”属性 // “withCredentials”仅存在于XMLHTTPRequest2对象里 xmlHttp = sendXmlHttp(xmlHttp,reqParam); } else if (typeof XDomainRequest != "undefined") { // 否则检查是否支持XDomainRequest,IE8和IE9支持 // XDomainRequest仅存在于IE中,是IE用于支持CORS请求的方式 xmlHttp = new XDomainRequest(); xmlHttp = sendXmlHttp(xmlHttp,reqParam); } else { //浏览器不支持CORS xmlHttp = null; } if (!xmlHttp) { throw new Error('CORS not supported'); } xmlHttp.onreadystatechange = function processRequest() { if (xmlHttp.readyState == 4) // 判断对象状态 { if (xmlHttp.status == 200) // 请求结果已经成功返回 { var responseObj = JSON.parse(xmlHttp.responseText); if(responseObj.code == 0){ console.log(responseObj.data); } else { console.log(responseObj.msg); } } } }; } //发送xmlHttp function sendXmlHttp(xmlHttp,reqParam){ var type = reqParam.type; if(type.toLowerCase() == 'post'){ xmlHttp.open("POST",reqParam.url,true); xmlHttp.setRequestHeader("Content-Type","application/x-www-form-urlencoded"); xmlHttp.send(reqParam.dataParam); } else if(type.toLowerCase() == 'get'){ xmlHttp.open("GET",reqParam.url,true); xmlHttp.setRequestHeader("Content-Type","application/x-www-form-urlencoded"); xmlHttp.send(null); } return xmlHttp; } //对象变成get参数形式 function dataToGetParam(data){ var dataParam = ''; if(data){ for(var key in data){ if(dataParam){ dataParam = dataParam + '&'+ key + '=' + data[key]; } else { dataParam = key + '=' + data[key]; } } } return dataParam; } ``` 与上文的js是基本一致的,唯一的区别就是在创建xmlHttp则要先判断浏览器是否支持cors,大家唯一担心的就是IE了,IE8及以上是可行的,所以也推荐大家使用啦。
如果觉得有帮忙,您可以在本页底部留言。
相关推荐:
NPM发包流程与技巧
Puppeteer爬取豆瓣电影TOP250评分
基于vue实现腾讯云点播的上传与播放
JS代理Proxy与反射Reflect场景
轻应用PWA实践全过程
ES6迭代器Iterator原理与性能
JavaScript之Set与Map
ES6设计模式之观察者模式
解决toFixed四舍五入陷阱
深入理解IEEE754的64位双精度
ES6设计模式之单例模式
ES6设计模式之工厂模式
ES6二叉树的实现
JavaScript链表实例
JavaScript排序算法及性能比较
JavaScript类的语法糖
……
更多
<
CSS变形matrix与动画cubic-bezier
JavaScript类的语法糖
>
全部留言
我要留言
内容:
网名:
邮箱:
个人网站:
发表
全部留言