Axios 或 fetch():您应该使用哪一个?

2025-05-25

Axios 或 fetch():您应该使用哪一个?

作者:法拉兹·凯尔希尼✏️

在我最近的文章“如何使用 Axios 像专业人士一样发出 HTTP 请求”中,我讨论了使用 Axios 库的优势。然而,必须承认 Axios 并非始终是理想的解决方案,有时还有更好的 HTTP 请求方案。

毫无疑问,一些开发者更喜欢 Axios,而不是内置 API,因为它易于使用。但许多人高估了对这样一个库的需求。该fetch()API 完全能够重现 Axios 的关键功能,并且它还具有在所有现代浏览器中均可轻松访问的额外优势。

在本文中,我们将比较fetch()Axios 和 JavaScript,并了解如何使用它们执行不同的任务。希望在阅读完本文后,您能够更好地理解这两个 API。

LogRocket 免费试用横幅

基本语法

在深入研究 Axios 的更多高级功能之前,让我们先将其基本语法与 进行比较fetch()。以下是如何使用 AxiosPOST向 URL 发送带有自定义标头的请求。Axios 会自动将数据转换为 JSON,因此您无需手动操作。

// axios

const options = {
  url: 'http://localhost/test.htm',
  method: 'POST',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json;charset=UTF-8'
  },
  data: {
    a: 10,
    b: 20
  }
};

axios(options)
  .then(response => {
    console.log(response.status);
  });
Enter fullscreen mode Exit fullscreen mode

现在将此代码与fetch()产生相同结果的版本进行比较:

// fetch()

const url = 'http://localhost/test.htm';
const options = {
  method: 'POST',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json;charset=UTF-8'
  },
  body: JSON.stringify({
    a: 10,
    b: 20
  })
};

fetch(url, options)
  .then(response => {
    console.log(response.status);
  });
Enter fullscreen mode Exit fullscreen mode

请注意:

  • 要发送数据,fetch()使用 body 属性,而 Axios 使用 data 属性
  • 中的数据fetch()是字符串化的
  • URL 作为参数传递给fetch()。然而,在 Axios 中,URL 是在 options 对象中设置的。

向后兼容性

Axios 的主要卖点之一是其广泛的浏览器支持。即使是像 IE11 这样的老旧浏览器也能流畅运行 Axios。而 Axios 仅支持 Chrome 42+、Firefox 39+、Edge 14+ 和 Safari 10.1+(您可以在“我可以使用吗Fetch()…… ”上查看完整的兼容性列表)。

如果您使用 Axios 的唯一原因是向后兼容,那么您实际上并不需要 HTTP 库。相反,您可以使用像这样fetch()的 polyfill在不支持的 Web 浏览器上实现类似的功能。要开始使用 fetch polyfill,请通过 npm 命令安装它:fetch()

npm install whatwg-fetch --save
Enter fullscreen mode Exit fullscreen mode

然后你可以发出这样的请求:

import 'whatwg-fetch'
window.fetch(...)
Enter fullscreen mode Exit fullscreen mode

请记住,在某些旧浏览器中您可能还需要承诺 polyfill。

响应超时

Axios 中设置超时的简便性是一些开发人员更喜欢它的原因之一fetch()。在 Axios 中,你可以使用 config 对象中的可选timeout属性来设置请求中止前的毫秒数。例如:

axios({
  method: 'post',
  url: '/login',
  timeout: 4000,    // 4 seconds timeout
  data: {
    firstName: 'David',
    lastName: 'Pollock'
  }
})
.then(response => {/* handle the response */})
.catch(error => console.error('timeout exceeded'))
Enter fullscreen mode Exit fullscreen mode

Fetch()通过AbortController界面提供类似的功能。但它不像 Axios 版本那么简单:

const controller = new AbortController();
const options = {
  method: 'POST',
  signal: controller.signal,
  body: JSON.stringify({
    firstName: 'David',
    lastName: 'Pollock'
  })
};  
const promise = fetch('/login', options);
const timeoutId = setTimeout(() => controller.abort(), 4000);

promise
  .then(response => {/* handle the response */})
  .catch(error => console.error('timeout exceeded'));
Enter fullscreen mode Exit fullscreen mode

AbortController这里,我们使用构造函数创建一个对象AbortController.AbortController(),以便稍后中止请求。signal是一个只读属性,用于AbortController提供与请求通信或中止请求的方法。如果服务器在四秒内没有响应,controller.abort()则会调用 ,并终止操作。

自动 JSON 数据转换

正如我们之前所说,Axios 在发送请求时会自动对数据进行字符串化(尽管你可以覆盖默认行为并定义其他转换机制)。fetch()然而,使用 时,你必须手动执行此操作。比较一下:

// axios
axios.get('https://api.github.com/orgs/axios')
  .then(response => {
    console.log(response.data);
  }, error => {
    console.log(error);
  });

// fetch()
fetch('https://api.github.com/orgs/axios')
  .then(response => response.json())    // one extra step
  .then(data => {
    console.log(data) 
  })
  .catch(error => console.error(error));
Enter fullscreen mode Exit fullscreen mode

数据自动转换是一项很好的功能,但同样,这并不是您无法做到的事情fetch()

HTTP 拦截器

Axios 的一大关键特性是能够拦截 HTTP 请求。当您需要检查或更改从应用程序到服务器(或反之亦然)的 HTTP 请求(例如日志记录、身份验证等)时,HTTP 拦截器会非常方便。使用拦截器,您无需为每个 HTTP 请求编写单独的代码。

以下是在 Axios 中声明请求拦截器的方法:

axios.interceptors.request.use(config => {
  // log a message before any HTTP request is sent
  console.log('Request was sent');

  return config;
});

// sent a GET request
axios.get('https://api.github.com/users/sideshowbarker')
  .then(response => {
    console.log(response.data);
  });
Enter fullscreen mode Exit fullscreen mode

在这段代码中,该axios.interceptors.request.use()方法用于定义在发送 HTTP 请求之前运行的代码。

默认情况下,fetch()不提供拦截请求的方法,但想出一个解决方法并不难。您可以覆盖全局 fetch 方法并定义自己的拦截器,如下所示:

fetch = (originalFetch => {
  return (...arguments) => {
    const result = originalFetch.apply(this, arguments);
      return result.then(console.log('Request was sent'));
  };
})(fetch);

fetch('https://api.github.com/orgs/axios')
  .then(response => response.json())
  .then(data => {
    console.log(data) 
  });
Enter fullscreen mode Exit fullscreen mode

下载进度

进度指示器在加载大型资源时非常有用,尤其是对于网速较慢的用户。以前,JavaScript 程序员使用XMLHttpRequest.onprogress回调处理程序来实现进度指示器。

Fetch API 没有处理程序。相反,它通过响应对象的 body 属性onprogress提供一个实例。ReadableStream

下面的例子说明了如何使用ReadableStream在图像下载过程中为用户提供即时反馈:

// original code: https://github.com/AnthumChris/fetch-progress-indicators

<div id="progress" src="">progress</div>
<img id="img">

<script>

'use strict'

const element = document.getElementById('progress');

fetch('https://fetch-progress.anthum.com/30kbps/images/sunrise-baseline.jpg')
  .then(response => {

    if (!response.ok) {
      throw Error(response.status+' '+response.statusText)
    }

    // ensure ReadableStream is supported
    if (!response.body) {
      throw Error('ReadableStream not yet supported in this browser.')
    }

    // store the size of the entity-body, in bytes
    const contentLength = response.headers.get('content-length');

    // ensure contentLength is available
    if (!contentLength) {
      throw Error('Content-Length response header unavailable');
    }

    // parse the integer into a base-10 number
    const total = parseInt(contentLength, 10);

    let loaded = 0;

    return new Response(

      // create and return a readable stream
      new ReadableStream({
        start(controller) {
          const reader = response.body.getReader();

          read();
          function read() {
            reader.read().then(({done, value}) => {
              if (done) {
                controller.close();
                return; 
              }
              loaded += value.byteLength;
              progress({loaded, total})
              controller.enqueue(value);
              read();
            }).catch(error => {
              console.error(error);
              controller.error(error)                  
            })
          }
        }
      })
    );
  })
  .then(response => 
    // construct a blob from the data
    response.blob()
  )
  .then(data => {
    // insert the downloaded image into the page
    document.getElementById('img').src = URL.createObjectURL(data);
  })
  .catch(error => {
    console.error(error);
  })

function progress({loaded, total}) {
  element.innerHTML = Math.round(loaded/total*100)+'%';
}

</script>
Enter fullscreen mode Exit fullscreen mode

在 Axios 中实现进度指示器更简单,尤其是使用Axios Progress Bar模块时。首先,您需要包含以下样式和脚本:

<link rel="stylesheet" type="text/css" href="https://cdn.rawgit.com/rikmms/progress-bar-4-axios/0a3acf92/dist/nprogress.css" />

<script src="https://cdn.rawgit.com/rikmms/progress-bar-4-axios/0a3acf92/dist/index.js"></script>
Enter fullscreen mode Exit fullscreen mode

然后你可以像这样实现进度条:

<img id="img">

<script>

loadProgressBar();

const url = 'https://fetch-progress.anthum.com/30kbps/images/sunrise-baseline.jpg';

function downloadFile(url) {
  axios.get(url, {responseType: 'blob'})
    .then(response => {
      const reader = new window.FileReader();
      reader.readAsDataURL(response.data); 
      reader.onload = () => {
        document.getElementById('img').setAttribute('src', reader.result);
      }
    })
    .catch(error => {
      console.log(error)
    });
}

downloadFile(url);

</script>
Enter fullscreen mode Exit fullscreen mode

此代码使用FileReaderAPI 异步读取已下载的图片。该readAsDataURL方法将图片数据以 Base64 编码的字符串形式返回,然后将其插入到标签src的属性中img以显示图片。

同时请求

为了同时发出多个请求,Axios 提供了axios.all()方法。只需将请求数组传递给此方法,然后使用axios.spread()它将响应数组的属性赋值给单独的变量:

axios.all([
  axios.get('https://api.github.com/users/iliakan'), 
  axios.get('https://api.github.com/users/taylorotwell')
])
.then(axios.spread((obj1, obj2) => {
  // Both requests are now complete
  console.log(obj1.data.login + ' has ' + obj1.data.public_repos + ' public repos on GitHub');
  console.log(obj2.data.login + ' has ' + obj2.data.public_repos + ' public repos on GitHub');
}));
Enter fullscreen mode Exit fullscreen mode

您可以使用内置Promise.all()方法实现相同的结果。将所有获取请求作为数组传递给Promise.all()。接下来,使用函数处理响应async,如下所示:

Promise.all([
  fetch('https://api.github.com/users/iliakan'),
  fetch('https://api.github.com/users/taylorotwell')
])
.then(async([res1, res2]) => {
  const a = await res1.json();
  const b = await res2.json();
  console.log(a.login + ' has ' + a.public_repos + ' public repos on GitHub');
  console.log(b.login + ' has ' + b.public_repos + ' public repos on GitHub');
})
.catch(error => {
  console.log(error);
});
Enter fullscreen mode Exit fullscreen mode

结论

Axios 以紧凑的封装形式提供了易于使用的 API,可满足您的大多数 HTTP 通信需求。但是,如果您更喜欢使用原生 API,那么您可以轻松实现 Axios 的功能。

正如本文所讨论的,使用 Web 浏览器提供的方法完全可以重现 Axios 库的关键功能fetch()。最终,是否值得加载客户端 HTTP API 取决于您是否习惯使用内置 API。


编者注:觉得这篇文章有什么问题?您可以在这里找到正确版本

插件:LogRocket,一个用于 Web 应用的 DVR

 
LogRocket 仪表板免费试用横幅
 
LogRocket是一款前端日志工具,可让您重放问题,就像它们发生在您自己的浏览器中一样。您无需猜测错误发生的原因,也无需要求用户提供屏幕截图和日志转储,LogRocket 允许您重放会话以快速了解问题所在。它可与任何应用程序完美兼容,无论使用哪种框架,并且提供插件来记录来自 Redux、Vuex 和 @ngrx/store 的更多上下文。
 
除了记录 Redux 操作和状态之外,LogRocket 还记录控制台日志、JavaScript 错误、堆栈跟踪、带有标头 + 正文的网络请求/响应、浏览器元数据以及自定义日志。它还会对 DOM 进行插桩,以记录页面上的 HTML 和 CSS,即使是最复杂的单页应用程序,也能重现像素完美的视频。
 
免费试用


文章Axios 或 fetch():您应该使用哪一个?最先出现在LogRocket 博客上。

文章来源:https://dev.to/bnevilleoneill/axios-or-fetch-which-should-you-use-95i
PREV
使用 Node.js 构建您自己的 Web 分析仪表板
NEXT
使用 React Hooks 和 GreenSock 实现动画