越来越火的网络请求Fetch和Axios到底有什么区别

在这几天由于编写脚本等一系列原因,不知怎么的突然发现现在很多主流的网站已经大量开始使用Fetch进行网络请求,感觉再不学习Fetch就要Out了,所以我花了一些时间专门去研究了一下关于Fetch的相关知识,发现Fetch被讨论的并不多,很多都是一年前甚至两年前的文章,大多数文章最后得出来的结论几乎都是Axios比Fetch好用。

事实确实如此,就我个人的体验来讲,Axios使用体验确实优于Fetch,那么为什么目前在很多大公司的网站上面都开始使用Fetch进行网络请求,带着这个疑问,我又去查找了很多资料,同时又自行将同样的请求使用Axios和Fetch进行尝试,最后得出一个结论:Fetch的优势仅仅在于浏览器原生支持。

对的,其实Fetch比起Axios来讲几乎没有任何优势(除了浏览器原生支持),Axios各个方面都比Fetch好用,Fetch要想实现Axios的一些功能还需要手动进行封装。

我截取了几个比较大的网站的请求图:

掘金:

image-20210226191506400

YouTube:

image-20210226191538119

知乎:

image-20210226191628939

需要注意的是:Axios是对XMLHttpRequest的封装,而Fetch是一种新的获取资源的接口方式,并不是对XMLHttpRequest的封装。

它们最大的不同点在于Fetch是浏览器原生支持,而Axios需要引入Axios库。

1. 火热程度

虽然无法进行直观的比较,但是我们可以从npm包下载量来看:

image-20210226192158770

因为Node环境下默认是不支持Fetch的,所以必须要使用node-fetch这个包,而这个包的周下载量一路攀升,可以看到已经来到了每周2千多万的下载量。这还仅仅是Node环境下,浏览器则是默认支持不需要第三方包。

image-20210226192350038

上面是Axios的下载量,可以看到也是一路攀升,Axios封装的各种方法确实非常的好用。


本篇文章着重会从下面几项内容进行比较:

  • 兼容性
  • 基本语法
  • 响应超时
  • 对数据的转化
  • HTTP拦截器
  • 同时请求

2. 兼容性问题

Axios可以兼容IE浏览器,而Fetch在IE浏览器和一些老版本浏览器上没有受到支持,但是有一个库可以让老版本浏览器支持Fetch即它就是whatwg-fetch,它可以让你在老版本的浏览器中也可以使用Fetch,并且现在很多网站的开发都为了减少成本而选择不再兼容IE浏览器。

注意:在比较旧的浏览器上面可能还需要使用promise兼容库。

各个浏览器对Fetch的兼容:

image-20210227000214141

3. 请求方式

下面我们来看一下如何使用Axios和Fetch进行请求。

Axios:

const options = {
  url: "http://example.com/",
  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);
});

Fetch:

const url = "http://example.com/";
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);
});

其中最大的不同之处在于传递数据的方式不同,Axios是放到data属性里,以对象的方式进行传递,而Fetch则是需要放在body属性中,以字符串的方式进行传递。

4. 响应超时

Axios的相应超时设置是非常简单的,直接设置timeout属性就可以了,而Fetch设置起来就远比Axios麻烦,这也是很多人更喜欢Axios而不太喜欢Fetch的原因之一。

axios({
  method: "post",
  url: "http://example.com/",
  timeout: 4000, // 请求4秒无响应则会超时
  data: {
    firstName: "David",
    lastName: "Pollock",
  },
})
  .then((response) => {
    /* 处理响应 */
  })
  .catch((error) => console.error("请求超时"));

Fetch提供了AbortController属性,但是使用起来不像Axios那么简单。

const controller = new AbortController();

const options = {
  method: "POST",
  signal: controller.signal,
  body: JSON.stringify({
    firstName: "David",
    lastName: "Pollock",
  }),
};
const promise = fetch("http://example.com/", options);

// 如果4秒钟没有响应则超时
const timeoutId = setTimeout(() => controller.abort(), 4000);

promise
  .then((response) => {
    /* 处理响应 */
  })
  .catch((error) => console.error("请求超时"));

5. 对数据的转化

Axios还有非常好的一点就是会自动对数据进行转化,而Fetch则不同,它需要使用者进行手动转化。

// axios
axios.get("http://example.com/").then(
  (response) => {
    console.log(response.data);
  },
  (error) => {
    console.log(error);
  }
);

// fetch
fetch("http://example.com/")
  .then((response) => response.json()) // 需要对响应数据进行转换
  .then((data) => {
    console.log(data);
  })
  .catch((error) => console.error(error));

Fetch提供的转化API有下面几种:

  • arrayBuffer()
  • blob()
  • json()
  • text()
  • formData()

使用Fetch时你需要清楚请求后的数据类型是什么,然后再用对应的方法将它进行转换。

Fetch可以通过一些封装实现Axios的自动转化功能,至于如何实现由于我没有去研究过所以就不再这里多嘴,不过实现起来应该不难,但是要将实现过程写的健壮就需要花费一定的时间。

6. HTTP拦截器

Axios的一大卖点就是它提供了拦截器,可以统一对请求或响应进行一些处理,相信如果看过一个比较完整的项目的请求封装的话,一定对Axios的拦截器有一定的了解,它是一个非常重要的功能。

使用它可以为请求附加token、为请求增加时间戳防止请求缓存,以及拦截响应,一旦状态码不符合预期则直接将响应消息通过弹框的形式展示在界面上,比如密码错误、服务器内部错误、表单验证不通过等问题。

axios.interceptors.request.use((config) => {
  // 在请求之前对请求参数进行处理
  return config;
});

// 发送GET请求
axios.get("http://example.com/").then((response) => {
  console.log(response.data);
});

而Fetch没有拦截器功能,但是要实现该功能并不难,直接重写全局Fetch方法就可以办到。

fetch = ((originalFetch) => {
  return (...arguments) => {
    const result = originalFetch.apply(this, arguments);
    return result.then(console.log("请求已发送"));
  };
})(fetch);

fetch("http://example.com/")
  .then((response) => response.json())
  .then((data) => {
    console.log(data);
  });

7. 同时请求

同时请求在项目中用的不多,但是偶尔可能会用到。

Axios:

axios
  .all([
    axios.get("https://api.github.com/users/iliakan"),
    axios.get("https://api.github.com/users/taylorotwell"),
  ])
  .then(
    axios.spread((obj1, obj2) => {
      ...
    })
  );

Fetch:

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();
  })
  .catch((error) => {
    console.log(error);
  });

8. 浏览器原生支持

Fetch唯一碾压Axios的一点就是现代浏览器的原生支持。

本着负责的态度(其实是因为这篇文章写得比较困难…因为我对Fetch的研究不深)在这几天,我多次尝试使用Fetch,习惯后觉得还挺好用的,最主要是浏览器原生就支持,不像Axios需要引入一个包,而且需要即时测试某些接口直接在Chrome浏览器中使用Fetch进行请求,尤其是编写爬虫或脚本的时候,你在当前网页打开Chrome的控制台使用Fetch几乎不需要什么配置就可以直接进行请求。

image-20210227211909726

上图是在知乎打开Chrome控制台然后调用知乎个人数据API,可以看到能够成功的拿到数据。

9. 最后

Fetch可以实现所有Axios能够实现的功能,但是需要自行进行封装,如果不喜欢折腾直接在项目中使用Axios是一个非常明智的选择,这完全取决于你是否愿意使用浏览器内置API。

有时候新技术逐渐取代老技术是一个必然趋势,所以Fetch有一天终将会取代XMLHttpRequest,也许之后Axios库会改为使用Fetch请求。

参考文章:Axios or fetch(): Which should you use?