9102年了XHR(jQuery)、Fetch和axios改选谁?

本文对比了XMLHttpRequest、Fetch和axios在数据请求上的优缺点。XMLHttpRequest设计混乱,jQuery虽简化但仍有问题。Fetch遵循Promise,实现更合理,而axios基于Promise,兼容性好且广泛应用。在选择时需考虑项目需求和兼容性问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

太长不看

在这里插入图片描述

  1. XMLHtppRequest:设计关注点不分离(数据输入/输出、状态变化追踪全部在一个对象里)、代码不具备时序性(由callback造成);
  2. jQuery-ajax:关注点不分离、代码非顺序执行、引入了非标准语义;
  3. Fetch:基于ES6 Promise设计关注点分离、支持ES6语法、代码具备时序性(async/await、promise)、易于同构、兼容性略低于XMLHtppRequest;
  4. axios:基于ES6Promise设计关注点分离、支持ES6语法、代码具备时序性、易于同构、封装完善久经沙场适合在生产环境使用;
  5. XMLHtppRequest年老色衰,Fetch初露锋芒,axios正当壮年;
  6. 本文完(大雾)

XMLHtppRequest

  • 扫盲:
    早期的网页但凡遇到获取数据的场景就要刷新页面,交互体验?没有的根本不存在。XMLHttpRequest(后文称XHR)就是此场景的解决方案用以改善体验。由XHR提供HTTP能力使浏览器与服务器产生交互(获取/提交数据),这些交互也不再需要刷新页面而只是局部更新。
    XHR是一个对象,这个对象下包含有许多“能力”,譬如:
    • Event handler
      事件处理回调:onloadstart、onprogress、onabort、onerror、onload、ontimeout、onloadend、onreadystatechange
    • readyState
      XHR的状态值:unsent(0)、opened(1)、headers received(2)、loading(3)、done(4)
    • Request Concept
      请求概念:XHR下的open、setRequestHeader、timeout、send、abort、withCrendtials等都属于这个概念;
    • Response Concept
      响应概念:XHR下的responseURL、status、response等都属于这个概念;

Event handler和readyState将不再赘述,值得关注的是Request和Response两个概念。为什么叫概念?
因为在XHR对象中并不存在名为Response/Request的属性/对象/方法。下图是XHR的内部构造示意图,可以看到XHR本身拥有非常多的属性、方法导致内部混乱它既有Request也有Response的方法,同时还包含状态追踪的EventHandler。正是因此XHR本身的实现略显混乱。
XMLHttpRequest内部实现
同时也导致XHR的代码在书写上更易混淆,譬如初学者就不容易分清readyState(xhr的状态)和status(网络请求的状态)、response和responseText等。基于回调的书写方式也增加了代码阅读难度,下面是使用XHR对象发起HTTP请求的简单例子:

// 老版本IE的兼容
var xhr
if (window.XMLHttpRequest) {  // Mozilla, Safari...
   xhr = new XMLHttpRequest();
} else if (window.ActiveXObject) { // IE
   try {
     xhr = new ActiveXObject('Msxml2.XMLHTTP');
   } catch (e) {
     try {
       xhr = new ActiveXObject('Microsoft.XMLHTTP');
     } catch (e) {}
   }
}
// onreadystatechange 方法
var onReadyStateChange = () => {
    if (!xhr || xhr.readyState !== 4) {
       return;
     }
     // 处理文件传输的特殊情况
     if (xhr.status === 0 && !(xhr.responseURL && xhr.responseURL.indexOf('file:') === 0)) {
       return;
     }
    var responseData = !xhr.responseType || xhr.responseType === 'text' ? xhr.responseText : xhr.response;

}
if (xhr) {
    // 注册回调函数
   xhr.onreadystatechange = onReadyStateChange;
   // 打开XHR
   xhr.open('POST', '/api', true);
   // 设置XHR的Header
   xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
   // 发送数据
   xhr.send('username=admin&password=root');
}

上述代码可以看出代码并不简洁、从上往下难以看出哪里在发请求、哪里在处理返回,同时代码执行顺序和书写顺序关联性弱。可能有人会说了:都9102年了!居然还有人写原生XHR!。下一个例子中使用jQuery发送请求,jQuery似乎将一团乱麻的代码收拢了,没有上一个例子那么杂乱,但其实并没有改善关注点不分离、不具备时序性(执行顺序和书写顺序)等问题,同时还引入了success、error、complete等“非官方”语义(虽然三者语义清晰,但fail和error、complete和done语义相近容易混淆并且done在jQuery中还真有这个方法)和ES6利用promise、async/await、then/catch/finally避免回调的理念相左。

   $.ajax({
   		    complete: function(XMLHttpRequest, textStatus){
               // do something after(wards) finished 
               // how do you know there is an afterwards?
            },
            type:"POST",
            url:"http://somewhere.far.away.from.here",
            data:{Name:"jhon snow",Password:"you know nothing"},
            error: function(){
               // do something when error is coming
            },
            success:function(data){
           		// do something after success
            },         
         });

下图是关于原生XHR和jQuery的代码书写和执行顺序的图,很明显无论是XHR还是jQuery代码数据顺序和(表面上的)执行顺序都是无关联的。
执行顺序和书写顺序

Fetch

XHR本身并不是ECMAScript的标准方法而是发展过程中先行于标准的各家浏览器的解决方案,随后再逐渐成为主流,但也因此导致部分浏览器“一枝独秀”(IE)搞得一件事情做了两遍,开发者也必须做“无意义”的兼容。而Fetch在标准先行的情况下,内部实现合理、实现了CORS协议、配合Promise语法和链式写法代码更清晰且有时序性,这些都是XHR所不具备的。

  • 内部实现
    Response和Request不再只是概念、ResponseBody单拎出来对其处理、拎出Header单独处理并具有守卫的概念,且Fetch内部都是基于Promise实现。代码书写也非常符合ES6的新理念。
    在这里插入图片描述

对比一下fetch和xhr感觉如下(有些夸装hhhh):
在这里插入图片描述

下面是简单的fetch请求,可以看出基于Promise使得关注点分离(数据的输入、输出和处理关注点分离)、语义清晰且正统(使用then、catch、finally而不是success、done、fail、error)、代码书写和执行顺序一致(先执行fetch再then再catch,跟我杠说内部还是回调的我可不理你)。

fetch("http://somewhere.far.from.here/some/path", {
  		method: "POST",
  		headers: {
    		"Content-Type": "application/x-www-form-urlencoded"
  		},
  		body: "name=Jaehaerys-Targaryen&password=you-konw-something"
  	})
	.then((res) => {
  		if (res.ok) {
    		alert("Daenerys gets iron throne");
  		} else if (res.status == 401) {
    		alert("Oops! Cersi gets iron throne.");
  		}
	})
	.catch(()=>{
 		alert("Night king kills everyone!");
	})

The fetch is so fetching,那我们应该马上开始使用fetch吗?如果这么快就马上使用那我下面两千字是干嘛的?
哼 !

axios

在这里插入图片描述
axios是一个基于Promise实现的同时为浏览器和Node服务端提供请求解决方案的库。下面是一个axios发起请求的例子:

    axios.post('http://somewhere/far/away/from/here', {
		name: 'Daenerys Targaryen',
		password: 'Daenerys Stormborn,the Unburnt,Queen of Meereen,Queen of the Andals and the Rhoynar and the First Men,Lord of the Seven Kingdoms,Protector of the Realm,Khaleesi of the Great Grass Sea,Breaker of Shackles,Mother of Dragons'
      })
      .then(res => {
        // response to my queen
      })
      .catch(err => {
        // I don't have that much rooms for you people
      })

上面代码可以看到axios语法简洁、支持async/await、Promise等ES6操作,且axios是基于Promise对XHR的封装所以兼容性比Fetch更好,同时在非常多项目中应用饱受考验完全已经是老司机了。
在这里插入图片描述
什么?你说你的项目是服务端渲染的?而Fetch同构更方便?下面代码是axios在发送请求前的代码,有一个叫adapter的东西。

dispatchRequest.js

  // do something else
 
  var adapter = config.adapter || defaults.adapter;
  return adapter(config).then(function onAdapterResolution(response) {
    // do something else 
  }, function onAdapterRejection(reason) {
    // do something else
  });

那这个adapter是什么?下面有一个方法,定睛一看!这不就是在Node环境获取http,别的环境获取xhr吗。对,axios不仅实现了浏览器端的请求同时也包办了Node环境的http请求,所以axios同样的能够方便的进行同构。

defaults.js

// something else 
function getDefaultAdapter() {
  var adapter;
  // Only Node.JS has a process variable that is of [[Class]] process
  if (typeof process !== 'undefined' && Object.prototype.toString.call(process) === '[object process]') {
    // For node use HTTP adapter
    adapter = require('./adapters/http');
  } else if (typeof XMLHttpRequest !== 'undefined') {
    // For browsers use XHR adapter
    adapter = require('./adapters/xhr');
  }
  return adapter;
}
// somethingelse

在这里插入图片描述

一些其他(无伤大雅的)细节

  • XHR在以下几种情况都不会被垃圾回收:
    • 如果一个XHR对象的state是opened/received/loading;
    • 如果在readystatechange/progress/abort/error/load/timeout/loaded上注册有event listeners;
    • axios在执行完一次请求后会对request置null,这样就能被标记清除垃圾回收了。
  • Origin/Referer/Cookie/Cookie2等是XHR的forbidden header name不能被js代码所设置。所以在现代浏览器中通过Referer头则能有效的防范CSRF/XSRF攻击;
  • XHR在请求时会主动带上同源cookie写在header中,Fetch在稍老版本浏览器中是默认不会携带cookie的但在新的浏览器中已经默认携带同源cookie;

参考资料

  1. whatwg–XMLHttpRequest
  2. whatwg–fetch
  3. MDN-XMLHttpRequest
  4. GoogleDeveloper-fetch

扩展学习

  1. JavaScript的垃圾回收
  2. 详细介绍XMLHttpRequest
  3. [详细介绍Fetch]:待填坑
  4. [axios源码分析]:待填坑
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值