原生JavaScript实现AJAX、JSONP


Posted in Javascript onFebruary 07, 2017

相信大多数前端开发者在需要与后端进行数据交互时,为了方便快捷,都会选择JQuery中封装的AJAX方法,但是有些时候,我们只需要JQuery的AJAX请求方法,而其他的功能用到的很少,这显然是没必要的。

其实,原生JavaScript实现AJAX并不难,这篇文章将会讲解如何实现简单的AJAX,还有跨域请求JSONP!

一、AJAX

AJAX的核心是XMLHttpRequest。

一个完整的AJAX请求一般包括以下步骤:

  • 实例化XMLHttpRequest对象
  • 连接服务器
  • 发送请求
  • 接收响应数据

我将AJAX请求封装成ajax()方法,它接受一个配置对象params。

function ajax(params) {  
 params = params || {};  
 params.data = params.data || {};  
 // 判断是ajax请求还是jsonp请求
 var json = params.jsonp ? jsonp(params) : json(params);  
 // ajax请求  
 function json(params) {  
  // 请求方式,默认是GET
  params.type = (params.type || 'GET').toUpperCase(); 
  // 避免有特殊字符,必须格式化传输数据 
  params.data = formatParams(params.data);  
  var xhr = null;  
  // 实例化XMLHttpRequest对象  
  if(window.XMLHttpRequest) {  
   xhr = new XMLHttpRequest();  
  } else {  
   // IE6及其以下版本  
   xhr = new ActiveXObjcet('Microsoft.XMLHTTP');  
  }; 
   // 监听事件,只要 readyState 的值变化,就会调用 readystatechange 事件
  xhr.onreadystatechange = function() { 
   // readyState属性表示请求/响应过程的当前活动阶段,4为完成,已经接收到全部响应数据
   if(xhr.readyState == 4) {  
    var status = xhr.status; 
    // status:响应的HTTP状态码,以2开头的都是成功
    if(status >= 200 && status < 300) {  
     var response = ''; 
     // 判断接受数据的内容类型 
     var type = xhr.getResponseHeader('Content-type');  
     if(type.indexOf('xml') !== -1 && xhr.responseXML) {  
      response = xhr.responseXML; //Document对象响应  
     } else if(type === 'application/json') {  
      response = JSON.parse(xhr.responseText); //JSON响应  
     } else {  
      response = xhr.responseText; //字符串响应  
     }; 
     // 成功回调函数 
     params.success && params.success(response);  
    } else {  
     params.error && params.error(status);  
    }  
   };  
  }; 
  // 连接和传输数据  
  if(params.type == 'GET') {
   // 三个参数:请求方式、请求地址(get方式时,传输数据是加在地址后的)、是否异步请求(同步请求的情况极少);
   xhr.open(params.type, params.url + '?' + params.data, true);  
   xhr.send(null);  
  } else {  
   xhr.open(params.type, params.url, true);  
   //必须,设置提交时的内容类型  
   xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8'); 
   // 传输数据 
   xhr.send(params.data);  
  }  
 } 
 //格式化参数  
 function formatParams(data) {  
  var arr = [];  
  for(var name in data) { 
   //  encodeURIComponent() :用于对 URI 中的某一部分进行编码
   arr.push(encodeURIComponent(name) + '=' + encodeURIComponent(data[name]));  
  };  
  // 添加一个随机数参数,防止缓存  
  arr.push('v=' + random());  
  return arr.join('&');  
 }
 // 获取随机数  
 function random() {  
  return Math.floor(Math.random() * 10000 + 500);  
 }
}

在上面的代码中readyState的值表示请求/响应过程的当前活动阶段,其值得含义如下:

0:为初始化。尚未调用open()方法。

1:启动。已经调用open方法,但尚未调用send()方法。

2:发送。已经调用send()方法,但尚未接受到响应。

3:接收。已经接收到部分响应数据。

4:完成。已经接收到全部响应数据,并且可以在客户端使用

使用实例:

ajax({  
 url: 'test.php',  // 请求地址
 type: 'POST',  // 请求类型,默认"GET",还可以是"POST"
 data: {'b': '异步请求'},  // 传输数据
 success: function(res){  // 请求成功的回调函数
  console.log(JSON.parse(res));  
 },
 error: function(error) {}  // 请求失败的回调函数
});

二、JSONP

同源策略

AJAX之所以需要“跨域”,罪魁祸首就是浏览器的同源策略。即,一个页面的AJAX只能获取这个页面相同源或者相同域的数据。 如何叫“同源”或者“同域”呢?——协议、域名、端口号都必须相同。例如

http://example.com  和  https://example.com 不同,因为协议不同;

http://localhost:8080  和  http://localhost:1000 不同,因为端口不同;

http://localhost:8080  和  https://example.com 不同,协议、域名、端口号都不同,根本不是一家的。

当跨域请求时,一般都会看到这个错误:

XMLHttpRequest cannot load http://ghmagical.com/article/?intro=jsonp%E8%AF%B7%E6%B1%82&v=5520. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost' is therefore not allowed access. 

那如何跨域请求呢?这时,JSONP就登场了!

JSONP是JSON with Padding(填充式JSON或参数式JSON)的简写,是一种非常常用的跨域请求方式。主要原理是利用了script 标签可以跨域请求的特性,由其 src 属性发送请求到服务器,服务器返回 JavaScript 代码,浏览器接受响应,然后就直接执行了,这和通过 script 标签引用外部文件的原理是一样的。

JSONP由两部分组成:回调函数和数据,回调函数是当响应到来时应该在页面中调用的函数,回调函数的名字一般在请求中指定。当服务器响应时,服务器端就会把该函数和数据拼成字符串返回。

JSONP的请求过程:

  • 请求阶段:浏览器创建一个 script 标签,并给其src 赋值(类似 http://example.com/api/?callback=jsonpCallback)。
  • 发送请求:当给script的src赋值时,浏览器就会发起一个请求。
  • 数据响应:服务端将要返回的数据作为参数和函数名称拼接在一起(格式类似”jsonpCallback({name: 'abc'})”)返回。当浏览器接收到了响应数据,由于发起请求的是 script,所以相当于直接调用 jsonpCallback 方法,并且传入了一个参数。

在这里讲解一下用原生JavaScript如何实现。

依旧是ajax()方法里添加JSONP,后面会将两者整合在一起,JSONP的配置参数主要多了一个jsonp参数,它就是你的回调函数名。

function ajax(params) {  
 params = params || {};  
 params.data = params.data || {};  
 var json = params.jsonp ? jsonp(params) : json(params);   
 // jsonp请求  
 function jsonp(params) {  
  //创建script标签并加入到页面中  
  var callbackName = params.jsonp;  
  var head = document.getElementsByTagName('head')[0];  
  // 设置传递给后台的回调参数名  
  params.data['callback'] = callbackName;  
  var data = formatParams(params.data);  
  var script = document.createElement('script');  
  head.appendChild(script);  
  //创建jsonp回调函数  
  window[callbackName] = function(json) {  
  head.removeChild(script);  
  clearTimeout(script.timer);  
  window[callbackName] = null;  
  params.success && params.success(json);  
  };  
//发送请求  
  script.src = params.url + '?' + data;  
  //为了得知此次请求是否成功,设置超时处理  
  if(params.time) {  
   script.timer = setTimeout(function() {  
    window[callbackName] = null;  
    head.removeChild(script);  
    params.error && params.error({  
     message: '超时'  
    });  
   }, time);  
  }  
 };  
 //格式化参数  
 function formatParams(data) {  
  var arr = [];  
  for(var name in data) {  
   arr.push(encodeURIComponent(name) + '=' + encodeURIComponent(data[name]));  
  };  
  // 添加一个随机数,防止缓存  
  arr.push('v=' + random()); 
  return arr.join('&');  
 }  
 // 获取随机数  
 function random() {  
  return Math.floor(Math.random() * 10000 + 500);  
 }
}

注意:因为 script 标签的 src 属性只在第一次设置的时候起作用,导致 script 标签没法重用,所以每次完成操作之后要移除;

使用实例:

ajax({  
  url: 'test',  // 请求地址
 jsonp: 'jsonpCallback', // 采用jsonp请求,且回调函数名为"jsonpCallbak",可以设置为合法的字符串
 data: {'b': '异步请求'},  // 传输数据
 success:function(res){  // 请求成功的回调函数
  console.log(res);  
 },
 error: function(error) {}  // 请求失败的回调函数
});

虽然JSONP非常简单易用,不过,它也存在两点不足

  • JSONP是从其他域加载代码执行,如果其他域不安全,可能会夹带一些恶意代码,而此时除了完全放弃JSONP调用之外,没办法追究
  • 要确认JSONP请求是否失败并不容易

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持三水点靠木!

Javascript 相关文章推荐
使用ExtJS技术实现的拖动树结点
Aug 05 Javascript
jquery插件tooltipv顶部淡入淡出效果使用示例
Dec 05 Javascript
jQuery插件jRumble实现网页元素抖动
Jun 05 Javascript
js精美的幻灯片画集特效代码分享
Aug 29 Javascript
AngularJS中处理多个promise的方式
Feb 02 Javascript
微信小程序 数据访问实例详解
Oct 08 Javascript
将json转换成struts参数的方法
Nov 08 Javascript
详解ECharts使用心得总结
Dec 06 Javascript
解决html input验证只能输入数字,不能输入其他的问题
Jul 21 Javascript
iview table render集成switch开关的实例
Mar 14 Javascript
vue使用ElementUI时导航栏默认展开功能的实现
Jul 04 Javascript
一个手写的vue放大镜效果
Aug 09 Javascript
[原创]SyntaxHighlighter自动识别并加载脚本语言
Feb 07 #Javascript
javascript表达式和运算符详解
Feb 07 #Javascript
利用jQuery实现滑动开关按钮效果(附demo源码下载)
Feb 07 #Javascript
原生js和css实现图片轮播效果
Feb 07 #Javascript
bootstrap输入框组使用方法
Feb 07 #Javascript
angularjs使用directive实现分页组件的示例
Feb 07 #Javascript
Bootstrap下拉菜单样式
Feb 07 #Javascript
You might like
thinkphp模板赋值与替换实例简述
2014/11/24 PHP
浅析Yii2缓存的使用
2016/05/10 PHP
PHP获取用户客户端真实IP的解决方案
2016/10/10 PHP
PHP获取input输入框中的值去数据库比较显示出来
2016/11/16 PHP
PHP7扩展开发之基于函数方式使用lib库的方法详解
2018/01/15 PHP
Laravel自定义 封装便捷返回Json数据格式的引用方法
2019/09/29 PHP
一个对于Array的简单扩展
2006/10/03 Javascript
jQuery 幻灯片插件(带缩略图功能)
2011/01/24 Javascript
jquery实现简单的拖拽效果实例兼容所有主流浏览器(优化篇)
2013/06/28 Javascript
js加入收藏以及使用Jquery更改透明度
2014/01/26 Javascript
jquery的clone方法应用于textarea和select的bug修复
2014/06/26 Javascript
小米公司JavaScript面试题
2014/12/29 Javascript
javascript实现加载xml文件的方法
2015/11/24 Javascript
基于Jquery和CSS3制作数字时钟附源码下载(CSS3篇)
2015/11/24 Javascript
学习JavaScript设计模式之代理模式
2016/01/12 Javascript
基于jQuery的select下拉框选择触发事件实例分析
2016/11/18 Javascript
JavaScript字符串对象(string)基本用法示例
2017/01/18 Javascript
原生js封装自定义滚动条
2017/03/24 Javascript
详细分析单线程JS执行问题
2017/11/22 Javascript
详解在React.js中使用PureComponent的重要性和使用方式
2018/07/10 Javascript
详解Vue-cli3.X使用px2rem遇到的问题
2019/08/09 Javascript
node解析修改nginx配置文件操作实例分析
2019/11/06 Javascript
vue 公共列表选择组件,引用Vant-UI的样式方式
2020/11/02 Javascript
[01:00:35]2018DOTA2亚洲邀请赛3月30日B组 EffcetVSMineski
2018/03/31 DOTA
python删除特定文件的方法
2015/07/30 Python
Python获取航线信息并且制作成图的讲解
2019/01/03 Python
Flask框架模板渲染操作简单示例
2019/07/31 Python
Pandas聚合运算和分组运算的实现示例
2019/10/17 Python
pytorch常见的Tensor类型详解
2020/01/15 Python
Tensorflow限制CPU个数实例
2020/02/06 Python
django Model层常用验证器及自定义验证器详解
2020/07/15 Python
24个canvas基础知识小结
2014/12/17 HTML / CSS
仓库文员岗位职责
2014/04/06 职场文书
乡镇党员群众路线教育实践活动对照检查材料思想汇报
2014/10/05 职场文书
毕业班工作总结
2015/08/10 职场文书
python和Appium的移动端多设备自动化测试框架
2022/04/26 Python