探秘 | 原生ajax、jquery-ajax、axios与fetch的区别与优缺点

目录

原生ajax

1. 什么是AJAX?

2. AJAX的使用

2.1 创建XHR对象

2.2 向服务器发送请求

2.3 服务器响应

2.4 onreadystatechange 事件

3. 手写一个简易的AJAX

现代AJAX的使用方式

1. jQuery封装的ajax

2. fetch API

3. axios


探秘 | 原生ajax、jquery-ajax、axios与fetch的区别与优缺点_第1张图片

原生ajax

现代浏览器,最开始与服务器交换数据,都是通过XMLHttpRequest对象。它可以使用JSON、XML、HTML和text文本等格式发送和接收数据。

多年来,XMLHttpRequest一直是web开发者的亲密助手。无论是直接的,还是间接的, 当我们谈及Ajax技术的时候,通常意思就是基于XMLHttpRequest的Ajax,它是一种能够有效改进页面通信的技术。 Ajax的兴起是由于Google的Gmail所带动的,随后被广泛的应用到众多的Web产品(应用)中,可以认为, 开发者已经默认将XMLHttpRequest作为了当前Web应用与远程资源进行通信的基础。

1. 什么是AJAX?

AJAX = Asynchronous JavaScript and XML(异步的 JavaScript 和 XML),意思是异步网络请求。区别于传统web开发中采用的同步方式。

AJAX 不是新的编程语言,而是一种使用现有标准的新方法。

AJAX 是与服务器交换数据并更新部分网页的艺术,在不重新加载整个页面的情况下。

Ajax带来的最大影响就是页面可以无刷新的请求数据。以往,页面表单提交数据,在用户点击完”submit“按钮后,页面会强制刷新一下,体验十分不友好。 有很多使用 AJAX 的应用程序案例:新浪微博、Google 地图、开心网等等。 可以这么说AJAX的出现极大了推动了前端的发展。

2. AJAX的使用

2.1 创建XHR对象

所有现代浏览器均支持 XMLHttpRequest 对象(IE5 和 IE6 使用 ActiveXObject)。XMLHttpRequest 用于在后台与服务器交换数据。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。 所有现代浏览器(IE7+、Firefox、Chrome、Safari 以及 Opera)均内建 XMLHttpRequest 对象。

let xmlhttp = new XMLHttpRequest()

老版本的 Internet Explorer (IE5 和 IE6)使用 ActiveX 对象:

let xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");

处理兼容性问题:

let xmlhttp;
if (window.XMLHttpRequest)
  {// code for IE7+, Firefox, Chrome, Opera, Safari
  xmlhttp = new XMLHttpRequest();
  }
else
  {// code for IE6, IE5
  xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
  }

2.2 向服务器发送请求

向服务器发送请求就要用到XMLHtttpRequest对象的open和send方法

xmlhttp.open("GET","test1.txt",true);
xmlhttp.send();

GET or POST ?

与 POST 相比,GET 更简单也更快,并且在大部分情况下都能用。

然而,在以下情况中,请使用 POST 请求:

  • 无法使用缓存文件(更新服务器上的文件或数据库)
  • 向服务器发送大量数据(POST 没有数据量限制)
  • 发送包含未知字符的用户输入时,POST 比 GET 更稳定也更可靠

2.3 服务器响应

如需获得来自服务器的响应,请使用 XMLHttpRequest 对象的 responseText属性。 responseText 属性返回字符串形式的响应,因此您可以这样使用:

document.getElementById("myDiv").innerHTML=xmlhttp.responseText;

2.4 onreadystatechange 事件

当请求被发送到服务器时,我们需要执行一些基于响应的任务。

每当 readyState 改变时,就会触发 onreadystatechange 事件。

readyState 属性存有 XMLHttpRequest 的状态信息。

下面是 XMLHttpRequest 对象的三个重要的属性:

属性 描述
onreadystatechange 存储函数(或函数名),每当 readyState 属性改变时,就会调用该函数。
readyState 存有 XMLHttpRequest 的状态。从 0 到 4 发生变化。- 0: 请求未初始化 1: 服务器连接已建立2: 请求已接收3: 请求处理中4: 请求已完成,且响应已就绪
status 200: "OK";404: 未找到页面

3. 手写一个简易的AJAX

这也算是面试中的一个高频考点了,虽然工作中不会中不会使用原生的AJAX而是使用封装好的AJAX,但是掌握基本原理还是必要的,这也应该是每位程序员的素养~

let xhr = new XMLHttpRequest();
xhr.open('GET', url, true)
xhr.onreadystatechange = function () {
  if (xhr.readyState === 4 && xhr.status === 200) {
    console.log(xhr.responseText);
  }else{
  console.log('404 NOT FOUND')
  }
}
xhr.send()

上面这种是最简易的,我们还可以结合Promise来实现,看下面一个版本

function ajax(url) {
  const p = new Promise((resolve, reject) => {
    let xhr = XMLHttpRequest()
    xhr.open("GET", url, true)
    xhr.onreadystatechange = function () {
      if (xhr.readyState === 4 && xhr.status === 200) {
        resolve(xhr.responseText)
      } else {
        reject(new Error("404 NOT FOUND"))
      }
    }
    xhr.send()
  })
  return p
}

番外:封装原生的请求

function obj2str(data) {
  data = data || {}; // 如果没有传参, 为了添加随机因⼦,必须⾃⼰创建⼀个对象
  data.t = new Date().getTime();
  var res = [];
  for (var key in data) {
    //在URL中是不可以出现中⽂的,如果出现了中⽂需要转码,可以调⽤encodeURIComponent⽅法,URL中
    res.push(encodeURIComponent(key) + "=" + encodeURIComponent(data[key]));
  }
  return res.join("&");
}

function ajax(option) {
  var str = obj2str(option.data); //key=value&key=value;
  var xmlhttp, timer;
  if (option.type.toLowerCase() === "get") {
    //toLowerCase将⼤写转化为⼩写
    xmlhttp.open(option.type, option.url + "?" + str, true);
    xmlhttp.send();
  } else {
    xmlhttp.open(option.type, option.url, true);
    //注意点:以下代码必须放在open和send之间
    xmlhttp.setRequestHeader(
      "Content-type",
      "application/x-www-form-urlencoded"
    );
    xmlhttp.send(str);
  }
  xmlhttp.onreadystatechange = function (ev2) {
    if (xmlhttp.readyState === 4) {
      clearInterval(timer);
      //判断是否请求成功(Http状态码⼤于等于200,且⼩于300,和状态码等于304为请求成功)
      if (
        (xmlhttp.status >= 200 && xmlhttp.status < 300) ||
        xmlhttp.status === 304
      ) {
        option.success(xmlhttp);
      } else {
        option.error(xmlhttp);
      }
    }
  };
  if (option.timeout) {
    timer = setInterval(function () {
      console.log("中断请求");
      xmlhttp.abort();
      clearInterval(timer);
    }, option.timeout);
  }
}

使用:

ajax({
  url:"http://server-name/login",
  type:'post',
  data:{
      username:'username',
      password:'password'
  },
  dataType:'json',
  timeout:10000,
  contentType:"application/json",
  success:function(data){
   console.log(data); //服务器返回响应,根据响应结果,分析是否登录成功
  },
  //异常处理
  error:function(e){
      console.log(e);
  }
})

 原生ajax的优点: 

  • 不重新加载页面的情况下更新网页
  • 在页面已加载后从服务器请求/接收数据
  • 在后台向服务器发送数据。

原生ajax的缺点:

  • 使用起来也比较繁琐,需要设置很多值。
  • 早期的IE浏览器有自己的实现,这样需要写兼容代码。

现代AJAX的使用方式

使用原生的js还是比较繁琐,实际工程中建议使用jQuery之类的库,封装的ajax请求方法非常好用,且解决了浏览器兼容性的问题。

1. jQuery封装的ajax

为了更快捷的操作DOM,并且规避一些浏览器兼容问题,产生了​​jQuery​​​。它里面的​​AJAX​​​请求也兼容了各浏览器,可以有简单易用的方法​​$.get​​​,​​$.post​​​。简单点说,就是对​​XMLHttpRequest​​对象的封装。

$.ajax({
  type: 'POST',
  url: url, 
  data: data,
  dataType: dataType,
  success: function () {},
  error: function () {}
})

jQuery-ajax的优点:

  • 对原生​​XHR​​的封装,做了兼容处理,简化了使用。
  • 增加了对​​JSONP​​的支持,可以简单处理部分跨域。

jQuery-ajax的缺点:

  • 如果有多个请求,并且有依赖关系的话,容易形成回调地狱。
  • 本身是针对MVC的编程,不符合现在前端MVVM的浪潮。
  • ajax是jQuery中的一个方法。如果只是要使用ajax却要引入整个jQuery非常的不合理。

关于jQuery中的AJAX:
由于jQuery的使用频率已经很低了,关于jQuery中的AJAX如何使用,可以参看:jQuery中如何发送ajax请求以及解决跨域问题,当然,心有余力的小伙伴们可以自行查阅jQuery关于ajax的相关资料。

2. fetch API

fetch号称是AJAX的替代品,是在ES6出现的,使用了ES6中的promise对象。Fetch是基于promise设计的。Fetch的代码结构比起ajax简单多了,参数有点像jQuery ajax。但是,一定记住:fetch不是ajax的进一步封装,而是原生的js,没有使用XMLHttpRequest对象。

fetch是前端发展的一种新技术产物,是XMLHttpRequest的最新替代技术,它是W3C的正式标准

以下内容摘自mozilla:

Fetch API 提供了一个 JavaScript接口,用于访问和操纵HTTP管道的部分,例如请求和响应。它还提供了一个全局 fetch()方法,该方法提供了一种简单,合理的方式来跨网络异步获取资源。

这种功能以前是使用 XMLHttpRequest实现的。Fetch提供了一个更好的替代方法,可以很容易地被其他技术使用,例如 Service Workers。Fetch还提供了单个逻辑位置来定义其他HTTP相关概念,例如CORS和HTTP的扩展。

在使用fetch的时候需要注意:

  • 当接收到一个代表错误的 HTTP 状态码时,从 fetch()返回的 Promise 不会被标记为 reject, 即使该 HTTP 响应的状态码是 404 或 500错误码(​​fetch只对网络请求报错,对​​400​​​,​​500​​都当做成功的请求,需要封装去处理)。相反,它会将 Promise 状态标记为 resolve (但是会将 resolve 的返回值的 ok 属性设置为 false ),仅当网络故障时或请求被阻止时,才会标记为 reject。
  • 默认情况下,fetch 不会从服务端发送或接收任何 cookies, 如果站点依赖于用户 session,则会导致未经认证的请求(要发送 cookies,必须设置 credentials 选项)。

​​​fetch​​​是低层次的API,代替​​XHR​​​,可以轻松处理各种格式,非文本化格式。可以很容易的被其他技术使用,例如​​Service Workers​​​。但是想要很好的使用​​fetch​​,需要做一些封装处理。

一个使用fetch获取数据的例子:

fetch('http://example.com/movies.json')   
  .then(function(response) {     
    return response.json();
  })   
  .then(function(myJson) {     
    console.log(myJson);
  });

fetch代表着更先进的技术方向,但是目前兼容性不是很好,在项目中使用的时候得慎重。

fetch 是比较新的技术,低版本浏览器和IE浏览器支持性不好 

fetch优点

①、语法简介,更加语义化
②、基于标准的promise实现,支持async/await
③、同构方便,使用isomorphic-fetch
④、更加底层,提供的API丰富
⑤、脱离了XHR,是ES规范里新的实现方式
⑥、其考虑到了传输的文件比较大的时候的处理

fetch缺点

fetch 是一个低层次的API,你可以把它考虑成原生的XHR,所以使用起来并不是那么舒服,需要进行封装。

①、当接收到一个代表错误的 HTTP 状态码时,从 fetch() 返回的 Promise 不会被标记为 reject,即使响应的 HTTP 状态码是 404 或 500错误码。相反,它会将 Promise 状态标记为 resolve (如果响应的 HTTP 状态码不在 200 - 299 的范围内,则设置 resolve 返回值的 ok 属性为 false ),仅当网络故障时或请求被阻止时,才会标记为 reject。
②、fetch 不会发送跨域 cookies(fetch​​​请求默认不会带​​cookie),需要添加配置项: fetch(url, {credentials: ‘include’})
③、fetch不支持abort,不支持超时控制,使用setTimeout及Promise.reject的实现的超时控制并不能阻止请求过程继续在后台运行,造成了流量的浪费
④、fetch没有办法原生监测请求的进度,而XHR可以

请注意,fetch规范与jQuery.ajax()主要有两种方式的不同,牢记:
 

- 当接收到一个代表错误的 HTTP 状态码时,从 fetch()返回的 Promise 不会被标记为 reject, 即使该 HTTP 响应的状态码是 404 或 500。相反,它会将 Promise 状态标记为 resolve (但是会将 resolve的返回值的 ok 属性设置为 false ),仅当网络故障时或请求被阻止时,才会标记为 reject。

- 默认情况下,fetch 不会从服务端发送或接收任何 cookies, 如果站点依赖于用户 session,则会导致未经认证的请求(要发送 cookies,必须设置 credentials 选项)。

‍♂️ 番外:为什么要使用fetch

XMLHttpRequest 是一个设计粗糙的 API,不符合关注分离(Separation of Concerns)的原则,配置和调用方式非常混乱,而且基于事件的异步模型写起来也没有现代的 Promise,generator/yield,async/await 友好。

Fetch 的出现就是为了解决 XHR 的问题,拿例子说明:

使用 XHR 发送一个 json 请求一般是这样:

var xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.responseType = 'json';

xhr.onload = function() {
  console.log(xhr.response);
};

xhr.onerror = function() {
  console.log("Oops, error");
};

xhr.send();

使用 Fetch 后,顿时看起来好一点 

fetch(url).then(function(response) {
  return response.json();
}).then(function(data) {
  console.log(data);
}).catch(function(e) {
  console.log("Oops, error");
});

使用 ES6 的 箭头函数 后: 

fetch(url).then(response => response.json())
  .then(data => console.log(data))
  .catch(e => console.log("Oops, error", e))

现在看起来好很多了,但这种 Promise 的写法还是有 Callback 的影子,而且 promise 使用 catch 方法来进行错误处理的方式有点奇怪。不用急,下面使用 async/await 来做最终优化: 

注:async/await 是非常新的 API,属于 ES7,这是它的完整规范。使用 Babel 开启 runtime 模式后可以把 async/await 无痛编译成 ES5 代码。也可以直接使用 regenerator 来编译到 ES5。

// 注:这段代码如果想运行,外面需要包一个 async function
try {
  let response = await fetch(url);
  let data = response.json();
  console.log(data);
} catch(e) {
  console.log("Oops, error", e);
}

duang~~ 的一声,使用 await 后,写异步代码就像写同步代码一样爽await 后面可以跟 Promise 对象,表示等待 Promise resolve() 才会继续向下执行,如果 Promise 被 reject() 或抛出异常则会被外面的 try...catch 捕获。

Promise,generator/yield,await/async 都是现在和未来 JS 解决异步的标准做法,可以完美搭配使用。这也是使用标准 Promise 一大好处。最近也把项目中使用第三方 Promise 库的代码全部转成标准 Promise,为以后全面使用 async/await 做准备。

另外,Fetch 也很适合做现在流行的同构应用,有人基于 Fetch 的语法,在 Node 端基于 http 库实现了 node-fetch,又有人封装了用于同构应用的 isomorphic-fetch。

注:同构(isomorphic/universal)就是使前后端运行同一套代码的意思,后端一般是指 NodeJS 环境。

3. axios

首先,你需要知晓:axios并不是一种新的技术,而只是一种技术方案。

axios 是一个基于Promise 用于浏览器和 nodejs 的 HTTP 客户端,本质上也是对原生XHR的封装,只不过它是Promise的实现版本,符合最新的ES规范,有以下特点:

  • 从浏览器中创建 XMLHttpRequests
  • 从 node.js 创建 http 请求
  • 支持 Promise API
  • 拦截请求和响应
  • 转换请求数据和响应数据
  • 取消请求
  • 自动转换 JSON 数据
  • 客户端支持防御 XSRF
  • 提供了一些并发请求的接口(重要,方便了很多的操作)

axios的使用方法

// 为给定 ID 的 user 创建请求
axios.get('/user?ID=12345')   
  .then(function (response) {     
    console.log(response);   
  })   
  .catch(function (error) {     
    console.log(error);   
  });  

// 上面的请求也可以这样做 
axios.get('/user', {     
   params: {      
     ID: 12345     
   }   
}).then(function (response) {    
   console.log(response);   
}).catch(function (error) {    
   console.log(error);   
});

关于更多axios的使用,大家请参考: axios官方文档​​​​​​

浏览器支持

axios面向现代浏览器设计(只支持现代浏览器),所以,古老的浏览器并不支持。

因为axios设计简洁,API简单,支持浏览器和node,所以大受欢迎。它能很好的与各种前端框架整合。因此,推荐大家在项目中使用axios库。

目前来看axios算是比较完美的一种方案了,几乎没有什么大的缺陷。

小结:

原生XHR几乎很少开发会用,JqueryAjax属于老当益壮的那种,虽然很老,但是很好用,Fetch是属于初生牛犊,还需要慢慢成长,axios就目前来说,算是非常好的了,无脑使用即可。

参考资料:Ajax、fetch、axios的区别与优缺点

探秘 | 原生ajax、jquery-ajax、axios与fetch的区别与优缺点_第2张图片

你可能感兴趣的:(JavaScript学习笔记,Vue.js学习笔记,ajax,jquery,javascript,Fetch,axios)