游侠网云服务,免实名免备案服务器 游侠云域名,免实名免备案域名

统一声明:

1.本站联系方式
QQ:709466365
TG:@UXWNET
官方TG频道:@UXW_NET
如果有其他人通过本站链接联系您导致被骗,本站一律不负责!

2.需要付费搭建请联系站长QQ:709466365 TG:@UXWNET
3.免实名域名注册购买- 游侠云域名
4.免实名国外服务器购买- 游侠网云服务
前端JavaScript异步请求两种方式详解|XHR与Fetch实战用法及区别

本文将从实际场景出发,先拆解XHR的传统实现(如状态监听、回调处理)与Fetch的现代语法(Promise链、async/await结合),再深度对比两者在错误处理、credentials传递、兼容性等关键维度的差异,帮你快速理清适用场景,避开开发踩坑,轻松掌握前端异步请求的核心技巧。

你有没有过这种情况?刚学前端时,写个异步请求要绕好几个弯——用XHR吧,得写一堆onreadystatechange监听,回调嵌套得像“回调地狱”;换Fetch吧,以为能轻松点,结果接口返回500错误,catch却纹丝不动,急得你对着控制台挠头?其实这俩工具都是前端异步请求的“老熟人”,但用法和坑点差得远呢,今天我就用自己踩过的坑、帮同事调过的bug,跟你唠唠它们的实战用法和区别。

XHR:传统异步的“老伙计”,该怎么用才不踩坑?

XHR(XMLHttpRequest)算是前端异步请求的“开国元勋”了,从IE6时代就有,直到现在还有不少项目在用——尤其是要兼容老浏览器的时候。我当年刚学前端时,第一次用XHR做登录功能,写了二十多行代码,结果拿到的响应是字符串,得自己JSON.parse,后来才知道要设置responseType: 'json',省了不少事。今天就跟你掰扯掰扯XHR的实战步骤,还有那些容易踩的“小陷阱”。

XHR的核心流程就四步:创建对象→配置请求→发送请求→监听结果。比如你要发个GET请求拿用户信息,代码大概长这样:

// 
  • 创建XHR对象
  • const xhr = new XMLHttpRequest();

    //

  • 配置请求:method(请求方式)、url(接口地址)、async(是否异步,默认true)
  • xhr.open('GET', '/api/user?id=1', true);

    //

  • (可选)设置请求头,比如JSON格式的POST请求需要加这个
  • xhr.setRequestHeader('Content-Type', 'application/json');

    //

  • (可选)设置响应类型,直接拿到JSON对象,不用自己转
  • xhr.responseType = 'json';

    //

  • 监听状态变化:readyState从0到4,4表示请求完成
  • xhr.onreadystatechange = function() {

    // 先判断请求完成(readyState===4),再判断HTTP状态码(200-299是成功)

    if (xhr.readyState === 4) {

    if (xhr.status >= 200 && xhr.status < 300) {

    // 拿到响应数据,因为设置了responseType,直接是对象

    console.log('请求成功:', xhr.response);

    } else {

    console.log('请求失败,状态码:', xhr.status);

    }

    }

    };

    //

  • 发送请求(GET请求不用传body,POST的话传JSON字符串)
  • xhr.send();

    是不是看着有点“繁琐”?但这些步骤里藏着很多容易踩的坑——比如去年帮一个刚入行的同事调代码,他的XHR请求一直没反应,我看了半天才发现:他漏写了send()方法!就像你买了快递单填完信息,却没交给快递员,能收到货才怪。

    再比如错误处理,XHR的onerror事件只处理“网络错误”(比如断网、跨域没配置),如果是HTTP状态码错误(比如404、500),onerror不会触发——得靠xhr.status判断。我之前做一个商品列表功能,接口返回500错误,我盯着onerror看了半天,结果发现得在onreadystatechange里判断状态码,才把问题解决。

    还有一个容易忘的点:POST请求的body格式。如果你要传JSON数据,得用JSON.stringify()转成字符串,还要设置Content-Type: application/json——我当年第一次做注册功能,直接把对象传给send(),结果后端收到的是[object Object],查了文档才知道要转格式。

    Fetch:现代异步的“新选择”,好用但得避开这些“隐形坑”

    Fetch是ES6之后出的“现代异步方案”,用Promise封装,语法比XHR简洁多了,现在很多框架(比如React、Vue)的异步请求都默认用它。但我得跟你说:Fetch不是“完美替代”,它的坑比你想象中多

    先看Fetch的基本用法——比如要拿用户列表,代码就这么几行:

    async function fetchUserList() {
    

    try {

    //

  • 发送请求,返回Promise(resolve的是Response对象)
  • const response = await fetch('/api/users', {

    method: 'GET', // 请求方式,默认GET

    headers: {

    'Content-Type': 'application/json'

    },

    credentials: 'include' // 携带Cookie,默认不携带!

    });

    //

  • 检查响应状态:response.ok是true表示200-299
  • if (!response.ok) {

    throw new Error(请求失败,状态码:${response.status});

    }

    //

  • 解析响应数据:json()返回Promise,拿到JSON对象
  • const data = await response.json();

    console.log('用户列表:', data);

    } catch (err) {

    console.log('请求出错:', err.message);

    }

    }

    是不是比XHR简洁多了?但这里藏着两个“致命坑”,我踩过好几次:

    坑1:HTTP错误不会触发catch

    Fetch的Promise只有在“网络错误”(比如断网)时才会reject,如果是HTTP状态码错误(比如404、500),它会resolve!我上次做一个订单详情页,接口返回500错误,catch却没触发,我以为是代码写错了,查了半小时才发现:得手动检查response.ok,否则错误不会进catch。就像你点外卖,商家给你送了份“变质的饭”(500错误),但外卖平台默认“已送达”,你得自己打开盒子看一眼才知道有问题。

    坑2:默认不携带Cookie

    Fetch默认不会带Cookie——如果你的接口需要用户登录状态(比如需要sessionId),得手动加credentials: 'include'参数。我之前做一个后台管理系统,用Fetch请求用户信息,结果一直返回“未登录”,后来才想起这个配置——就像你去餐厅吃饭,忘了带会员卡,服务员肯定不认识你啊。

    坑3:不支持进度监听(默认)

    XHR可以用onprogress监听请求进度(比如文件上传的进度条),但Fetch默认不支持——得用ReadableStream。比如你要做图片上传进度条,可以这么写:

    const fileInput = document.querySelector('input[type="file"]');
    

    fileInput.addEventListener('change', async (e) => {

    const file = e.target.files[0];

    const formData = new FormData();

    formData.append('file', file);

    // 用Fetch的body是ReadableStream,监听进度

    const response = await fetch('/api/upload', {

    method: 'POST',

    body: formData,

    // 监听上传进度

    signal: AbortController().signal, // 可选:取消请求

    onUploadProgress: (progressEvent) => {

    const percent = (progressEvent.loaded / progressEvent.total) 100;

    console.log(上传进度:${percent}%);

    }

    });

    });

    不过这个功能是Fetch的“高级用法”,一般小项目用不到,但如果你做文件上传,可得记着这个点。

    除了坑,Fetch也有不少优点:比如支持Stream(处理大文件更高效)、语法更简洁(结合async/await几乎没有嵌套)、API更现代化(比如Headers对象管理请求头)。我现在做的Vue项目,所有异步请求都用Fetch,代码比用XHR时少了三分之一,可读性高多了。

    最后:XHR和Fetch该怎么选?一张表帮你理清楚

    说了这么多,你肯定想问:“我到底该用XHR还是Fetch?”我整理了一张对比表,从实战场景、坑点、兼容性三个维度帮你选:

    对比项 XHR Fetch
    兼容性 支持IE6+,老浏览器首选 支持Chrome 42+、Firefox 39+,IE不支持
    错误处理 onerror(网络错误)+ status(HTTP错误) 需手动检查response.ok,否则不触发catch
    Cookie携带 默认携带 需设置credentials: 'include'
    进度监听 onprogress直接用,简单 需用ReadableStream,略复杂
    语法复杂度 代码多,回调嵌套多 Promise+async/await,简洁易读

    简单 (不是 哦):如果你的项目要兼容IE11或更老的浏览器,直接选XHR;如果是现代浏览器(比如Chrome、Edge),选Fetch更舒服——但得记着那些坑。比如我最近做的移动端H5项目,用户都是用微信或手机浏览器,直接用Fetch,代码写得快,也没兼容问题;但上个月帮一个国企做后台系统,他们要求支持IE11,我就老老实实用XHR,再加个polyfill处理Promise

    你最近用XHR或Fetch时踩过什么坑?比如有没有遇到过Fetch不触发catch的情况?或者XHR忘了设置responseType?欢迎留言告诉我,咱们一起避坑——毕竟前端的坑,踩一次就够了!


    你要是做项目碰到得兼容IE11的情况,问能不能用Fetch?说实话,直接用肯定不行——IE11根本就不认识Fetch这个API,你写了Fetch的代码,打开IE11一试,控制台直接蹦“Fetch is undefined”的错误,白忙活一场。那有没有办法曲线救国?有的,加个polyfill比如whatwg-fetch,它能模拟Fetch的基本用法,比如发个GET请求拿用户列表,或者POST提交个表单数据,这些简单场景没问题。但你得记着,polyfill只是“模拟”,不是真的Fetch——像进度监听这种高级功能它就搞不定,比如你要做个图片上传的进度条,用polyfill的Fetch肯定拿不到进度数据,到时候又得改代码换方法,反而更费时间。

    其实我 你直接用XHR得了,毕竟XHR是IE11原生就支持的,不用额外加任何依赖,稳得很。你想啊,兼容IE11的项目本来就讲究“稳定大于一切”,XHR虽然写起来繁琐点——比如要创建对象、open配置、onreadystatechange监听状态,还要自己判断status码是不是200-299——但胜在“老而可靠”,不会出那种“Chrome里跑通了,IE11里直接崩”的幺蛾子。我之前帮一个国企做后台系统,他们要求必须支持IE11,我一开始图新鲜用了Fetch加polyfill,结果做文件上传功能时,进度条死活不显示,查了半天文档才发现,polyfill根本不支持Fetch的ReadableStream进度监听,最后没办法,还是换回XHR,用onprogress事件才搞定。再说了,XHR的用法虽然老,但网上资料多到爆,碰到问题随便搜搜就能找到解决办法,比折腾polyfill省心多了——毕竟做兼容项目,“少踩坑”比“用新东西”重要多了。


    XHR和Fetch应该根据什么场景选择?

    如果项目需要兼容IE11或更老的浏览器(如IE6-10),优先选XHR;如果是现代浏览器(Chrome、Edge、Firefox等),Fetch的语法更简洁,结合async/await更易读,适合用Fetch。简单说,老项目兼容选XHR,新项目现代浏览器选Fetch。

    为什么Fetch请求返回500错误却不触发catch?

    Fetch的Promise只有在“网络错误”(如断网、跨域配置错误)时才会reject触发catch;如果是HTTP状态码错误(如404、500),Fetch会默认resolve。解决方法是手动检查响应对象的ok属性——如果response.ok为false(状态码不在200-299之间),就手动抛出错误。

    XHR怎么监听文件上传或下载的进度?

    XHR可以通过onprogress事件监听进度:创建XHR对象后,给xhr.onprogress赋值一个函数,函数里的progressEvent对象有loaded(已传输字节数)和total(总字节数)属性,用这两个值就能计算进度(比如loaded/total100)。

    Fetch为什么默认不带Cookie?怎么解决?

    Fetch默认的credentials配置是“same-origin”(只在同域名下带Cookie),如果需要跨域或同域都携带Cookie,要在Fetch的配置项里加credentials: 'include'——这样请求就会自动带上Cookie,保持用户登录状态。

    需要兼容IE11,还能用Fetch吗?

    不能,IE11本身不支持Fetch API。如果一定要用类似Fetch的语法,可以加polyfill(比如whatwg-fetch),但polyfill只能模拟Fetch的基本功能,像进度监听的高级特性还是不支持;更稳妥的方式是直接用XHR,毕竟XHR是IE11原生支持的。