【Nuxt系列文章】一文读懂Nuxt3的网络请求

举报
liuming9157 发表于 2023/05/15 11:53:46 2023/05/15
【摘要】 Nuxt3如何整合Axios?我的答案是,不要在Nuxt3里整合Axios.虽说Axios很好用,并且多数同学都已经很熟悉Axios的使用。但是Nuxt3官方团队已经不建议使用Axios。本文将向各位童鞋介绍:1.为何官方不推荐Axios,2 如何在Nuxt3中封装类似Axios一样的功能。

大家好,我是刘明,十年创业老兵,开源技术爱好者。
有童鞋问我,Nuxt3如何整合Axios?我的答案是,不要在Nuxt3里整合Axios.
虽说Axios很好用,并且多数同学都已经很熟悉Axios的使用。但是Nuxt3官方团队已经不建议使用Axios。本文将向各位童鞋介绍:

  1. 为何官方不推荐Axios
  2. 如何在Nuxt3中封装类似Axios一样的功能。

Axios的前世今生

Axios的诞生

在早期,浏览器中发起网络请求需要用到XMLHttpRequest 对象,在node后端环境中发起网络请求需要用到http模块。两者写法不一样,用起来也比较复杂。于是Axios对两者做个一个封装,统一了前后端的写法。
对于开发者而言,只要用上了Axios,就不需要再区分是node还是浏览器环境,反正二者写法上都是一样的。不仅如此,Axios还有请求拦截和响应拦截等高级功能。所以,Axios很快就流行起来。Nuxt2也有整合好的Axios模块,并且官方也曾大力推荐。

Axios的落伍

到了Nuxt3,时代变了。
首先是浏览器环境。浏览器原生支持fetch,并且有代替XMLHttpRequest的趋势。
紧接着是Node.2022年2月,Node v17.5发布,引入了对fetch的原生支持。
至此,fetch在前后端大统一了。
回头再看,Axios前端封装XMLHttpRequest后端封装http的做法就完全没有必要了。

如何看待Axios

虽然fetch在前后端大统一,但是还有很多童鞋喜欢使用Axios.这是正常的,毕竟Axios确实很好用,而且很多老项目仍然需要维护。
以至于,我在网上看到一些文章,拿Axios和Fetch进行对比的时候,得出的结论是Axios比fetch好。其实这是一个非常错误的对比。Axios是一个封装好的项目,fetch只是一个原生API.如果非要对比的话,应该拿XMLHttpRequest对象和fetch比或者http模块与fetch比。
随着时间推移,越来越多的新项目会推荐使用fetch.Nuxt3是2022年诞生的新项目,完全放弃使用Axios。

Nuxt3的官方做法

拿Axios和Fetch进行对比并得出结论说Axios更好用,虽然是一个错误的对比。但至少说明一点,fetch要想真正获得认可,应该有一个非常好用的封装。Nuxt3的官方团队考虑到了这一点,将fetch进一步封装,开发了一个新项目叫做ofetch,并且将ofetch集成到Nuxt3中。Nuxt3中有个随处可用的全局对象$fetch,就是ofetch.
我看过ofetch的全部代码,与Axios相比代码量少,Axios有的功能ofetch也基本都有。官方在Nuxt3中默认集成了ofetch,ofetch已经是Nuxt3项目不可缺少的一部分。所以他们自然不再推荐Axios.

如何在Nuxt3中封装出类似Axios一样的功能

Nuxt3官方文档DataFeching部分,介绍了useFech和$fetch的简单用法,如果是刚刚上手Nuxt3的童鞋,强烈建议仔细阅读此部分。
但是很多项目还需要设置baseURL,还需要请求拦截和相应拦截。这些功能在官方文档中都没有给出代码案例。
实现起来很简单,只需要再写一个useRequest的组合函数即可。在Nuxt3中,组合函数是自动导入的,所以写出来就可以用了。

//文件位置:/composables/useRequest.ts
type Response = {
  url: string;
  body:any,
  status: number;
  type:string,
  statusText?: string;
  _data?: any;
  headers?:object,
  ok?:boolean,
  redirected?:boolean,
  bodyUsed?:boolean,
};
type ResponseData={
  code:number,
  msg:string,
  data:object|object[]
}

export const useRequest = async (url: string,options:object) => {
  const router = useRouter();
  //此处是引入了pinia进行状态管理,大家可以根据自己需求进行重写
  //const store = useMainStore();
  const host = window.location.hostname;
  const headers = {
    Authorization:'Bearer '+localStorage.getItem('token')||null,
  };
  const defaultOptions:object = {
    //baseURL也可以在nuxt.config.ts中定义然后此处引入
    baseURL: "http://example.com",
    headers: headers,
    //响应拦截
    onResponse({ response }: { response: Response }) {
      console.log("response", response);
      const res = response._data;
      //后端返回code=0时弹出错误信息,此处采用了element-plus
      if (res.code == 0) {
        ElMessage.error(res.msg);
      }
    },
    //响应错误拦截
    onResponseError({ response }: { response: Response }) {
      console.log("response-error", response);
      const res = response._data;
      //后端返回401时导航到登录界面
      if (response.status == 401) {
        router.replace("/login");
        //store.logout()
      }
    },
  };
  const newOptions:object={...defaultOptions,...options};
  //采用element-plus进行请求时的加载
  //const loadingInstance = ElLoading.service({fullscreen:false});
  const { data, pending,refresh } = await useFetch(url, newOptions);
  if (!pending.value) {
    //loadingInstance.close();
  }
  return {data,refresh};
};

以上封装代码采用的是TypeScript写法,如果不习惯TypeScript,可以私信我获取javascript写法
这个封装中,主要实现了以下几个功能:

  1. 添加了全局的baseURL,
  2. 在请求中添加了全局的header,header中包含token,用于后端的身份认证
  3. 添加了响应拦截,对于错误信息进行了统一提示,并对401错误自动转入登录界面
  4. 网络请求时增加了elemeng-plus的laoding组件,自动处理loading的打开和关闭

这个封装其实就是对于官方useFetch的进一步加工,封装后,返回的仍然是useFetch的data和refresh.使用方法和useFetch基本一致。
如果你对useFetch的使用还不熟悉,直接查看官方文档。
文档链接:useFetch使用文档

以上代码,只是给一个example,童鞋们完全可以根据自己的需求进行重写。比如,再写一个useGet或者usePost组合函数,以免每次请求都需要选择method.或者,再增加一些复杂的请求拦截.
重点是,一定要弄懂ofetch的用法。只有这样,才能根据自己的需求,随意改造代码。

我是刘明,十年创业老兵,开源技术爱好者。无论你是交流学习,还是有开发需求,欢迎私信联系。
有问题,找老刘

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。