基于本文回答

播面 播面

刷题像听歌,多听自然懂
0
评论

XMLHttpRequest 和 fetch 的区别是什么?

知识点图片

XMLHttpRequest (XHR) 和 fetch 都是用于在浏览器中发起网络请求(AJAX)的 API,但它们属于不同的时代,设计理念也有很大区别。

简单来说:XMLHttpRequest 是老旧的、基于事件的 API;而 fetch 是现代的、基于 Promise 的 API。

以下是它们的主要区别:

1. 语法与设计模式 (Syntax & Design)

  • XMLHttpRequest:
    • 基于事件 (Event-based): 你需要监听 onreadystatechangeonload 等事件来处理结果。
    • 回调地狱: 在处理多个依赖请求时,容易导致代码嵌套过深,难以维护。
    • 配置繁琐,对象状态复杂。
  • Fetch:
    • 基于 Promise: 原生支持 Promise,可以链式调用 .then(),或者配合 async/await 使用,代码更加简洁、线性。
    • 设计更加模块化(Request, Response, Headers 对象分离)。

2. 错误处理 (Error Handling) —— 核心区别

这是开发者从 XHR 转到 Fetch 时最容易踩的坑。

  • XMLHttpRequest:
    • 如果请求失败(网络错误)或者服务器返回错误状态码(如 404, 500),你通常需要在回调函数里手动判断 status
  • Fetch:
    • Fetch 认为只要服务器返回了响应,请求就是成功的。
    • 即便服务器返回 404500 错误,Fetch 返回的 Promise 状态依然是 resolved (成功)
    • Fetch 只有在网络故障(如 DNS 解析失败、断网)或请求被阻止时,Promise 才会 reject (失败)
    • 处理方式: 你必须手动检查 response.ok 属性(当 status 在 200-299 之间时为 true)来判断业务逻辑是否成功。

3. Cookie 和凭证 (Cookies & Credentials)

  • XMLHttpRequest:
    • 默认情况下,同源请求会自动携带 Cookie。跨域请求如果配置了 withCredentials = true 也会携带。
  • Fetch:
    • 默认不发送 Cookie(即使是同源请求,早期标准也不发送,现代标准同源默认发送,但跨域绝对不发)。
    • 如果你需要发送 Cookie(特别是跨域时),必须显式配置 credentials: 'include'

4. 数据处理 (Data Handling)

  • XMLHttpRequest:
    • 处理 JSON 数据比较麻烦,通常需要手动 JSON.parse(xhr.responseText)
  • Fetch:
    • 提供了方便的方法直接转换数据流,如 response.json(), response.text(), response.blob(), response.formData() 等。

5. 进度监测 (Progress Monitoring)

  • XMLHttpRequest:
    • 优势: 原生支持上传和下载的进度监控 (onprogress 事件)。这在做文件上传进度条时非常有用。
  • Fetch:
    • 劣势: Fetch 本身没有原生的 onprogress 事件。
    • 虽然可以通过 response.body.getReader() (ReadableStream) 来实现下载进度监控,但实现起来比 XHR 复杂得多。
    • 上传进度目前 Fetch 标准支持依然不完善(通常还是退回到 XHR 处理大文件上传)。

6. 请求中止 (Aborting Requests)

  • XMLHttpRequest:
    • 直接调用 xhr.abort() 即可,非常简单。
  • Fetch:
    • 需要配合 AbortControllersignal 来实现,稍微繁琐一点,但这是通用的取消异步操作的标准。

7. 兼容性 (Compatibility)

  • XMLHttpRequest: 兼容所有浏览器,包括古老的 IE。
  • Fetch: 现代浏览器支持良好,但在 IE 中无法使用(需要引入 Polyfill,如 whatwg-fetch)。

代码对比

XMLHttpRequest 写法

javascript
const xhr = new XMLHttpRequest();
xhr.open('GET', '/api/data');

xhr.onload = function() {
  if (xhr.status >= 200 && xhr.status < 300) {
    // 成功,手动解析 JSON
    const data = JSON.parse(xhr.responseText);
    console.log(data);
  } else {
    // 处理 HTTP 错误 (404, 500 等)
    console.error('Error:', xhr.statusText);
  }
};

xhr.onerror = function() {
  // 处理网络错误
  console.error('Network Error');
};

xhr.send();

Fetch 写法 (使用 async/await)

javascript
async function getData() {
  try {
    const response = await fetch('/api/data', {
      // 如果需要携带 cookie
      // credentials: 'include' 
    });

    // 关键:手动检查 HTTP 状态码
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }

    // 自动解析 JSON
    const data = await response.json();
    console.log(data);

  } catch (error) {
    // 捕获网络错误 或 上面抛出的 HTTP 错误
    console.error('Fetch failed:', error);
  }
}

getData();

总结:该用哪个?

  • 绝大多数情况:使用 fetch(或者基于 fetch 封装的库,如 Axios)。它更现代、代码更易读、功能更强大。
  • 特殊情况:使用 XMLHttpRequest。如果你需要监控上传进度(例如上传大文件并显示进度条),或者必须兼容非常老的浏览器且不想用 Polyfill,XHR 依然是唯一的选择。
00:00
00:00