【源码阅读】比官网更全的useFetch使用方式(超详细)
自从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使用方式,超详细
我们先看函数申明。
从函数声明我们可以看出来,useFecth有三种传参方式。
第一种,最简单,直接useFetch('/test')。**
第二种, 传url,以及useFetch配置参数,也是我开头用的方式。**
useFecth('/test', { immediate: false })
下面详细来看一下useFetch的参数
1.fetch(请求api函数,默认为window.fetch)
也就是说可以用其他的请求函数,但是里面的数据处理方式都是fetch的。暂时想不到应用场景,但是这是一个好的处理方式,可以脱离浏览器环境使用。
2.immediate(是否立即请求接口,默认为true,如果为false,则需要调用execute)
3.refetch(当url以及refetch值发生改变时,是否重新发起请求,默认为false)
4.initialData(data的初始值)
5.timeout(延迟取消接口请求,abort函数取消接口请求)
6.beforeFetch, afterFetch, onFetchError
钩子函数,在请求之前,请求之后,请求错误的时候执行钩子函数,比如beforeFetch就会把钩子函数返回的值跟原有的配置进行合并。
第三种,传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方法的。
其次是UseFetchReturn。
上面蓝色的都是返回的属性,黄色的是方法,可以说非常全了。
第四种用法,传url以及useFetch参数,request参数通过特定的方法进行调用
useFetch('test', { immediate: false }).post({})
源码是如何实现的?
核心的源码其实就是useFetch函数,之前上面也简单的看了一些。
1.参数处理以及数据定义
这部分没啥好说的,因为useFetch接收的参数个是是不固定的,所以针对参数不同的情况需要做一些处理。其次是变量申明,申明了一些状态值,以及数据容器。
2.函数定义
剩下的代码中,主要就是定义方法,还没执行到,先不管。(标红的地方前文也提到过了)
3.执行execute
Promise.resolve().then(() => execute())这行代码,使用了微任务执行。
在函数内部,abort取消接口发送,然后设置各种值,以及钩子函数,比如beforeFetch
4.abort
通过AbortController把当前请求取消。(如果对AbortController可以百度一下,后面有机会再聊)
5.fetch
不管怎么封装,想要请求api,肯定还是使用fetch方法。
之前提到的初始值,以及几个钩子函数都是在这里执行的。
fetchResponseconfig.type就是对数据text或json处理。
一个简单的fetch函数,又被封装成了用不起的样子。
更伤的是,官网上对useFetch的介绍真的不多,特别是一些复杂场景的时候。
我之前用的时候就感觉不太方便,于是想看个源码自己封装一套。
然后完源码之后,嗯,发现其实还挺好用的。(就是没用对,果然小丑就是我自己)
转载自:https://juejin.cn/post/7238911457847623736