我将
useFetch()
包装为可组合项,以提供自定义基本 URL 并自动设置身份验证令牌,但是当我在不带 await
关键字的组件中调用可组合项时,它的行为不会按预期响应。通常,我可以调用 const { data, pending, status } = useFetch('...')
,一旦 Promise 解决且 data
可用,UI 就会更新,但对于包装器来说这是行不通的。看来我必须始终将 await
与 useCustomFetch()
一起使用
知道为什么这行不通吗?
下面是自定义包装。
import type { UseFetchOptions } from 'nuxt/app'
import { defu } from 'defu'
export async function useCustomFetch<T>(url: string, options: UseFetchOptions<T> = {}) {
const config = useRuntimeConfig()
const { data, status, getSession }: { data: any; status: any; getSession: Function } = useAuth()
const headers: HeadersInit | undefined = {}
// NOTE: Set 'Authorization' header
if (data && status.value === 'authenticated') {
const session = await getSession({ required: true })
headers.Authorization = `Bearer ${session.accessToken}`
}
const defaults: UseFetchOptions<T> = {
baseURL: config.public.baseApiUrl,
headers,
onResponse(_ctx) {
// EX: _ctx.response._data = new myBusinessResponse(_ctx.response._data)
},
onResponseError(_ctx) {
// TODO: Send notification to Sentry
},
}
// NOTE: Deep defaults, use unjs/defu
const params = defu(options, defaults)
return useFetch(url, params)
}
在组件中,这不起作用。
<script setup>
const { data, pending, status } = useCustomFetch('/resources')
</script>
useAsyncData
和其他依赖它的可组合项(在本例中为 useFetch
)返回增强的承诺。 async
函数始终返回本机 Promise,不会影响其工作方式,但没有来自增强 Promise 的 data
等。为了保留原始行为,useCustomFetch
应该使用原始承诺并实现增强。
function useCustomFetch<T>(url: string, options: UseFetchOptions<T> = {}) {
let promise = Promise.resolve<unknown>(null);
if (data && status.value === 'authenticated') {
promise = promise
.then(() => getSession({ required: true }))
.then(session => {
headers.Authorization = `Bearer ${session.accessToken}`
})
}
...
promise = promise
.then(() => useFetch(url, params));
Object.assign(promise, ???);
return promise as ReturnType<(typeof useFetch)<T>;
}
可以看出这种方法会产生问题,因为当结果立即返回时不存在要扩充的属性。这将需要通过创建新的
data
等参考并将其结果同步到 useFetch
的结果来增加另一个级别的复杂性。 await getSession
使实现变得不必要的复杂,并且在没有 await
的情况下使用可组合项时可能会导致不正确的行为。
getSession
调用可能应该移至 fetch 选项,然后这变得简单,这允许推断结果的类型:
function useCustomFetch<T>(url: string, options: UseFetchOptions<T> = {}) {
...
const defaults: UseFetchOptions<T> = {
async onRequest(_ctx) {
const session = await getSession({ required: true })
_ctx.options = {
..._ctx.options.headers,
_ctx.options.Authorization: `Bearer ${session.accessToken}`
}
},
...
return useFetch(url, params);
}
请注意,
文档中未列出增强承诺和不带
await
的用法。由于这没有记录,因此不应依赖它,并且可能会更改,恕不另行通知。
如果请求应该延迟执行而不等待结果,
useFetch
,因此自定义可组合项可以与 lazy
选项一起使用:
await useCustomFetch({ lazy: true, ... })