XMLHttpRequest 和 fetch API 有什么区别?fetch API 的优势和局限性是什么?

各位好,欢迎来到今天的“前端老司机带你飞”系列讲座。今天我们要聊的是Web数据请求界的两位“大佬”:XMLHttpRequest (简称XHR) 和 Fetch API。他们就像是古代的信鸽和现代的快递小哥,都是负责把信息从服务器安全又快速地送到我们面前的。

一、XMLHttpRequest (XHR):元老级的信鸽

XHR,可以说是Web开发的元老级人物了。它出现得很早,曾经是浏览器端发起HTTP请求的唯一选择。你可以把它想象成一只训练有素的信鸽,你给它写好信(请求),绑在它的腿上,它飞到服务器,拿到回信(响应),再飞回来给你。

XHR的特点:

  • 兼容性好: 几乎所有浏览器都支持,老古董级别的浏览器也能用。
  • 使用繁琐: 代码写起来比较冗长,嵌套回调让人头疼。
  • 事件驱动: 通过监听各种事件来处理请求的状态变化(比如请求开始、数据加载中、请求完成等)。
  • 不支持Promise: 这是个硬伤,导致异步操作处理起来不够优雅。

XHR的代码示例:

function getSomething(url) {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();

    xhr.open('GET', url);

    xhr.onload = function() {
      if (xhr.status >= 200 && xhr.status < 300) {
        resolve(xhr.response);
      } else {
        reject(new Error('Request failed with status: ' + xhr.status));
      }
    };

    xhr.onerror = function() {
      reject(new Error('Network error'));
    };

    xhr.send();
  });
}

getSomething('https://api.example.com/data')
  .then(data => {
    console.log('Data received:', data);
  })
  .catch(error => {
    console.error('Error:', error);
  });

可以看到,虽然我们用Promise封装了一下,但里面的xhr.onloadxhr.onerror仍然是传统的事件处理方式。

二、Fetch API:风华正茂的快递小哥

Fetch API 是一个比较新的标准,它的目标是提供一个更现代、更强大的方式来进行网络请求。你可以把它想象成一个快递小哥,装备了先进的物流系统,能够更高效、更便捷地送达包裹。

Fetch API 的特点:

  • 基于Promise: 使用Promise进行异步处理,代码更简洁,更易于理解和维护。
  • 更强大的功能: 支持流式请求和响应,可以更好地处理大型文件。
  • 模块化设计: 将请求和响应分离,更易于扩展和定制。
  • 默认不发送Cookie: 需要手动设置 credentials: 'include' 才能发送Cookie。
  • 网络错误处理: Fetch只会在网络错误时reject Promise,HTTP状态码错误(比如404、500)不会reject,需要手动判断。
  • 兼容性相对较差: 虽然现代浏览器都支持,但老版本浏览器需要polyfill。

Fetch API 的代码示例:

fetch('https://api.example.com/data')
  .then(response => {
    if (!response.ok) {
      throw new Error('Network response was not ok ' + response.status);
    }
    return response.json(); // 或者 response.text(),取决于返回的数据类型
  })
  .then(data => {
    console.log('Data received:', data);
  })
  .catch(error => {
    console.error('Error:', error);
  });

可以看到,Fetch API的代码更加简洁明了,Promise的使用让异步操作的处理更加优雅。

三、XHR vs. Fetch API:信鸽和快递小哥的对决

特性 XMLHttpRequest (XHR) Fetch API
异步处理 事件驱动回调 Promise
代码简洁性 冗长 简洁
功能 相对简单 更强大(流式处理等)
兼容性 很好 较好(需要polyfill)
Cookie处理 默认发送Cookie 默认不发送,需要手动设置 credentials
错误处理 网络错误和HTTP状态码错误都触发错误事件 仅网络错误reject Promise,HTTP状态码需要手动判断
上传/下载进度 支持 支持(需要配合Streams API)

四、Fetch API 的优势:

  1. Promise的加持: 这是Fetch API最大的优势。Promise让异步代码更加可读、可维护,避免了回调地狱。

  2. 简洁的语法: Fetch API的语法更加简洁明了,更容易上手。

  3. 更强大的功能: Fetch API支持流式请求和响应,可以更好地处理大型文件,这在XHR时代是比较麻烦的。

  4. 模块化设计: Fetch API将请求和响应分离,使得我们可以更容易地扩展和定制请求和响应的处理流程。

  5. 清晰的关注点分离: Fetch API 更加关注 HTTP 协议本身,将底层细节隐藏起来,开发者可以专注于业务逻辑。

五、Fetch API 的局限性:

  1. 兼容性问题: 虽然现代浏览器都支持Fetch API,但对于一些老版本浏览器,需要使用polyfill才能正常工作。

  2. 错误处理: Fetch API 的错误处理机制比较特殊,它只会在网络错误时reject Promise,HTTP状态码错误(比如404、500)不会reject,需要手动判断response.ok。这是一个常见的“坑”,需要特别注意。

  3. 默认不发送Cookie: Fetch API 默认不发送Cookie,需要手动设置 credentials: 'include' 才能发送。这在某些场景下可能会导致问题。

  4. 不支持 AbortController 在所有浏览器上的完美支持: 虽然 AbortController 允许你取消 fetch 请求,但在某些老旧浏览器上可能存在兼容性问题。

  5. 流式 API 的复杂性: 虽然 Fetch API 支持流式请求和响应,但要真正用好 Streams API,需要一定的学习成本。

六、Fetch API 的“坑”和注意事项:

  1. response.ok 是关键: 一定要检查 response.ok 的值,判断HTTP状态码是否正常。

  2. credentials: 'include' 如果需要发送Cookie,记得加上这个选项。

  3. 处理不同类型的数据: 根据返回的数据类型,选择合适的处理方式,比如 response.json()response.text()response.blob() 等。

  4. 错误处理要全面: 除了处理网络错误,还要处理HTTP状态码错误,以及JSON解析错误等。

  5. 注意请求头: Fetch API 默认会添加一些请求头,比如 Accept,如果需要自定义请求头,可以使用 headers 选项。

七、代码示例:更复杂的 Fetch 请求

async function postData(url, data) {
  try {
    const response = await fetch(url, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': 'Bearer YOUR_API_KEY' // 添加授权头
      },
      body: JSON.stringify(data),
      credentials: 'include' // 发送Cookie
    });

    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }

    const responseData = await response.json();
    return responseData;
  } catch (error) {
    console.error('There was an error!', error);
    throw error; // 重新抛出错误,方便上层处理
  }
}

// 使用示例
const data = {
  name: 'John Doe',
  age: 30
};

postData('https://api.example.com/users', data)
  .then(data => {
    console.log('Success:', data);
  })
  .catch(error => {
    console.error('Error:', error);
  });

这个例子展示了如何使用 Fetch API 发送 POST 请求,设置请求头,发送Cookie,以及处理错误。注意 async/await 的使用,让异步代码更加简洁易懂。

八、何时选择 XHR,何时选择 Fetch API?

  • 兼容性至上: 如果需要兼容老版本浏览器,或者需要在一些特殊的环境中使用(比如一些老的移动端webview),那么 XHR 仍然是首选。

  • 追求简洁和现代: 如果你的项目只需要支持现代浏览器,并且你希望代码更加简洁易读,那么 Fetch API 是更好的选择。

  • 需要更强大的功能: 如果你的项目需要处理大型文件,或者需要更灵活的请求和响应处理,那么 Fetch API 的流式处理能力会让你事半功倍。

  • 现有的代码库: 如果你已经有一个基于 XHR 的成熟的代码库,并且没有特别的理由进行重构,那么继续使用 XHR 也是可以的。

九、总结:

XHR 和 Fetch API 都是Web开发中重要的工具,它们各有优缺点,适用于不同的场景。XHR 就像是一位经验丰富的老兵,虽然有些老旧,但仍然可靠;Fetch API 就像是一位充满活力的年轻人,更加现代、更加强大。选择哪个,取决于你的具体需求和偏好。

记住,没有最好的工具,只有最合适的工具。希望今天的讲解能够帮助你更好地理解和使用这两个“大佬”,在Web开发的道路上越走越远!

好了,今天的讲座就到这里,感谢大家的聆听!希望下次有机会再和大家分享更多前端开发的经验和技巧。祝大家编码愉快!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注