likes
comments
collection
share

【源码阅读】比官网更全的useFetch使用方式(超详细)

作者站长头像
站长
· 阅读数 45

自从vue3支持hook之后,如果说选一个我最想要的一个功能,那一定是useFetch。

实在是太方便,太省代码了。

我们看个简单的例子就知道了。

// 常规写法
const loading = ref(false)
const error = ref(null)
const data = ref([])

fetch().then((res) => {
  loading.value = true
  data.value = res
}).catch(err => {
  error.value = err
})

// 使用hook
const { isFetching, error, data } = useFetch(url)

是不是看着很简洁?

特别是请求特别多的时候,真的是谁用谁知道。

因为我之前用的是axios,用fetch会略有不习惯(比如params参数需要自己拼到url上,fetch默认返回text,需要使用json方法)

如果你之前用的就是fetch,那就真的只有好处了。

PS(我在使用中有一个不太舒服的地方)

const form = reactive({
  name: "",
  password: "",
  checkPassword: ""
})

const { isFetching, error, data, execute } = useFetch(url, {
  immediate: false
}).post({
  username: form.name,
  password: form.password
})

// 用户提交
const submit = () => {
    execute()
}

当用户点提交的时候,执行接口请求。

逻辑上没有问题,但是useFetch请求的一直是空的值。因为传入的值没有修改,(如果直接传入form是可以取到值的,因为form是响应式的。但是在实际开发过程中,表单数据经常会有冗余,比如checkPassword,又不想传到后端去)

目前的解决方案有两个。

方案1: 写一个计算属性,取代参数对象

方案2: 配置beforeFetch钩子函数,每次请求前,重新获取值。

const { error, data, execute } = useMyFetch(
  '/test',
  {
    method: 'post'
  },
  {
    immediate: false,
    beforeFetch: (ctx) => {
      ctx.options.body = JSON.stringify({
        name: user.email,
        password: user.password
      })
      return ctx
    }
  }
)

又或者,我们可以自己封装一个hook,毕竟每个人习惯不一样,也没有尽善尽美的工具。

好了,废话就不说了,下面开始源码部分。


为了尽量了解并使用useFetch。

源码部分会分2部分,第一部分是根据源码,解锁useFetch的各种用法,第二部分解锁这些用法的实现原理

useFetch使用方式,超详细

【源码阅读】比官网更全的useFetch使用方式(超详细)

我们先看函数申明。

【源码阅读】比官网更全的useFetch使用方式(超详细)

从函数声明我们可以看出来,useFecth有三种传参方式。

第一种,最简单,直接useFetch('/test')。**

第二种, 传url,以及useFetch配置参数,也是我开头用的方式。**

useFecth('/test', { immediate: false  })

下面详细来看一下useFetch的参数

1.fetch(请求api函数,默认为window.fetch)

也就是说可以用其他的请求函数,但是里面的数据处理方式都是fetch的。暂时想不到应用场景,但是这是一个好的处理方式,可以脱离浏览器环境使用。

2.immediate(是否立即请求接口,默认为true,如果为false,则需要调用execute)

【源码阅读】比官网更全的useFetch使用方式(超详细)

3.refetch(当url以及refetch值发生改变时,是否重新发起请求,默认为false)

【源码阅读】比官网更全的useFetch使用方式(超详细)

4.initialData(data的初始值)

【源码阅读】比官网更全的useFetch使用方式(超详细)

5.timeout(延迟取消接口请求,abort函数取消接口请求)

【源码阅读】比官网更全的useFetch使用方式(超详细)

6.beforeFetch, afterFetch, onFetchError

钩子函数,在请求之前,请求之后,请求错误的时候执行钩子函数,比如beforeFetch就会把钩子函数返回的值跟原有的配置进行合并。

【源码阅读】比官网更全的useFetch使用方式(超详细)

第三种,传url,request参数以及useFetch配置参数

useFetch('test', { method: "post" }, { immediate: false })

useFetch配置参数上面已经讲过了,下面看request参数。

这个就是我们平时接触比较多的request参数了,包括请求方式,请求头,请求参数等,这个就不一一介绍了。

interface RequestInit {
    /** A BodyInit object or null to set request's body. */
    body?: BodyInit | null;
    /** A string indicating how the request will interact with the browser's cache to set request's cache. */
    cache?: RequestCache;
    /** A string indicating whether credentials will be sent with the request always, never, or only when sent to a same-origin URL. Sets request's credentials. */
    credentials?: RequestCredentials;
    /** A Headers object, an object literal, or an array of two-item arrays to set request's headers. */
    headers?: HeadersInit;
    /** A cryptographic hash of the resource to be fetched by request. Sets request's integrity. */
    integrity?: string;
    /** A boolean to set request's keepalive. */
    keepalive?: boolean;
    /** A string to set request's method. */
    method?: string;
    /** A string to indicate whether the request will use CORS, or will be restricted to same-origin URLs. Sets request's mode. */
    mode?: RequestMode;
    /** A string indicating whether request follows redirects, results in an error upon encountering a redirect, or returns the redirect (in an opaque fashion). Sets request's redirect. */
    redirect?: RequestRedirect;
    /** A string whose value is a same-origin URL, "about:client", or the empty string, to set request's referrer. */
    referrer?: string;
    /** A referrer policy to set request's referrerPolicy. */
    referrerPolicy?: ReferrerPolicy;
    /** An AbortSignal to set request's signal. */
    signal?: AbortSignal | null;
    /** Can only be null. Used to disassociate request from any Window. */
    window?: null;
}

然后看一下返回值,返回的是UseFetchReturn和PromiseLike的联合类型。

PromiseLike并不是一个promsie类型,而是一个带有then方法的,类似Promise的一个类型,也就是说使用useFetch是没有catch方法的。

【源码阅读】比官网更全的useFetch使用方式(超详细)

其次是UseFetchReturn。

【源码阅读】比官网更全的useFetch使用方式(超详细)

【源码阅读】比官网更全的useFetch使用方式(超详细)

上面蓝色的都是返回的属性,黄色的是方法,可以说非常全了。

第四种用法,传url以及useFetch参数,request参数通过特定的方法进行调用

 useFetch('test', { immediate: false }).post({})

源码是如何实现的?

核心的源码其实就是useFetch函数,之前上面也简单的看了一些。

1.参数处理以及数据定义

【源码阅读】比官网更全的useFetch使用方式(超详细)

这部分没啥好说的,因为useFetch接收的参数个是是不固定的,所以针对参数不同的情况需要做一些处理。其次是变量申明,申明了一些状态值,以及数据容器。

2.函数定义

【源码阅读】比官网更全的useFetch使用方式(超详细)

剩下的代码中,主要就是定义方法,还没执行到,先不管。(标红的地方前文也提到过了)

3.执行execute

Promise.resolve().then(() => execute())这行代码,使用了微任务执行。

在函数内部,abort取消接口发送,然后设置各种值,以及钩子函数,比如beforeFetch 【源码阅读】比官网更全的useFetch使用方式(超详细)

4.abort

通过AbortController把当前请求取消。(如果对AbortController可以百度一下,后面有机会再聊)

【源码阅读】比官网更全的useFetch使用方式(超详细)

5.fetch

不管怎么封装,想要请求api,肯定还是使用fetch方法。

【源码阅读】比官网更全的useFetch使用方式(超详细)

之前提到的初始值,以及几个钩子函数都是在这里执行的。

fetchResponseconfig.type就是对数据text或json处理。


一个简单的fetch函数,又被封装成了用不起的样子。

更伤的是,官网上对useFetch的介绍真的不多,特别是一些复杂场景的时候。

我之前用的时候就感觉不太方便,于是想看个源码自己封装一套。

然后完源码之后,嗯,发现其实还挺好用的。(就是没用对,果然小丑就是我自己)

转载自:https://juejin.cn/post/7238911457847623736
评论
请登录