feat(nuxt)!: use paylod cache for initial data fetching by default (#3985)

Co-authored-by: Daniel Roe <daniel@roe.dev>
This commit is contained in:
pooya parsa 2022-03-31 12:24:28 +02:00 committed by GitHub
parent 58386198c9
commit 323803832a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 39 additions and 14 deletions

View File

@ -16,12 +16,12 @@ Within your pages, components and plugins you can use `useAsyncData` to get acce
const { const {
data: Ref<DataT>, data: Ref<DataT>,
pending: Ref<boolean>, pending: Ref<boolean>,
refresh: (force?: boolean) => Promise<void>, refresh: () => Promise<void>,
error?: any error?: any
} = useAsyncData( } = useAsyncData(
key: string, key: string,
handler: (ctx?: NuxtApp) => Promise<Object>, handler: (ctx?: NuxtApp) => Promise<Object>,
options?: { options?: {
lazy: boolean, lazy: boolean,
server: boolean, server: boolean,
watch: WatchSource[] watch: WatchSource[]
@ -40,6 +40,7 @@ const {
* _transform_: a function that can be used to alter `handler` function result after resolving * _transform_: a function that can be used to alter `handler` function result after resolving
* _pick_: only pick specified keys in this array from `handler` function result * _pick_: only pick specified keys in this array from `handler` function result
* _watch_: watch reactive sources to auto refresh * _watch_: watch reactive sources to auto refresh
* _initialCache_: When set to `false`, will skip payload cache for initial fetch. (defaults to `true`)
Under the hood, `lazy: false` uses `<Suspense>` to block the loading of the route before the data has been fetched. Consider using `lazy: true` and implementing a loading state instead for a snappier user experience. Under the hood, `lazy: false` uses `<Suspense>` to block the loading of the route before the data has been fetched. Consider using `lazy: true` and implementing a loading state instead for a snappier user experience.

View File

@ -21,12 +21,17 @@ export interface AsyncDataOptions<
transform?: Transform transform?: Transform
pick?: PickKeys pick?: PickKeys
watch?: MultiWatchSources watch?: MultiWatchSources
initialCache?: boolean
}
export interface RefreshOptions {
_initial?: boolean
} }
export interface _AsyncData<DataT> { export interface _AsyncData<DataT> {
data: Ref<DataT> data: Ref<DataT>
pending: Ref<boolean> pending: Ref<boolean>
refresh: (force?: boolean) => Promise<void> refresh: (opts?: RefreshOptions) => Promise<void>
error?: any error?: any
} }
@ -58,6 +63,7 @@ export function useAsyncData<
console.warn('[useAsyncData] `defer` has been renamed to `lazy`. Support for `defer` will be removed in RC.') console.warn('[useAsyncData] `defer` has been renamed to `lazy`. Support for `defer` will be removed in RC.')
} }
options.lazy = options.lazy ?? (options as any).defer ?? false options.lazy = options.lazy ?? (options as any).defer ?? false
options.initialCache = options.initialCache ?? true
// Setup nuxt instance payload // Setup nuxt instance payload
const nuxt = useNuxtApp() const nuxt = useNuxtApp()
@ -81,11 +87,15 @@ export function useAsyncData<
error: ref(nuxt.payload._errors[key] ?? null) error: ref(nuxt.payload._errors[key] ?? null)
} as AsyncData<DataT> } as AsyncData<DataT>
asyncData.refresh = (force?: boolean) => { asyncData.refresh = (opts = {}) => {
// Avoid fetching same key more than once at a time // Avoid fetching same key more than once at a time
if (nuxt._asyncDataPromises[key] && !force) { if (nuxt._asyncDataPromises[key]) {
return nuxt._asyncDataPromises[key] return nuxt._asyncDataPromises[key]
} }
// Avoid fetching same key that is already fetched
if (opts._initial && options.initialCache && nuxt.payload.data[key] !== undefined) {
return nuxt.payload.data[key]
}
asyncData.pending.value = true asyncData.pending.value = true
// TODO: Cancel previous promise // TODO: Cancel previous promise
// TODO: Handle immediate errors // TODO: Handle immediate errors
@ -115,11 +125,13 @@ export function useAsyncData<
return nuxt._asyncDataPromises[key] return nuxt._asyncDataPromises[key]
} }
const initialFetch = () => asyncData.refresh({ _initial: true })
const fetchOnServer = options.server !== false && nuxt.payload.serverRendered const fetchOnServer = options.server !== false && nuxt.payload.serverRendered
// Server side // Server side
if (process.server && fetchOnServer) { if (process.server && fetchOnServer) {
const promise = asyncData.refresh() const promise = initialFetch()
onServerPrefetch(() => promise) onServerPrefetch(() => promise)
} }
@ -131,10 +143,10 @@ export function useAsyncData<
} else if (instance && (nuxt.isHydrating || options.lazy)) { } else if (instance && (nuxt.isHydrating || options.lazy)) {
// 2. Initial load (server: false): fetch on mounted // 2. Initial load (server: false): fetch on mounted
// 3. Navigation (lazy: true): fetch on mounted // 3. Navigation (lazy: true): fetch on mounted
instance._nuxtOnBeforeMountCbs.push(asyncData.refresh) instance._nuxtOnBeforeMountCbs.push(initialFetch)
} else { } else {
// 4. Navigation (lazy: false) - or plugin usage: await fetch // 4. Navigation (lazy: false) - or plugin usage: await fetch
asyncData.refresh() initialFetch()
} }
if (options.watch) { if (options.watch) {
watch(options.watch, () => asyncData.refresh()) watch(options.watch, () => asyncData.refresh())

View File

@ -7,11 +7,16 @@ import { useAsyncData } from './asyncData'
export type FetchResult<ReqT extends FetchRequest> = TypedInternalResponse<ReqT, unknown> export type FetchResult<ReqT extends FetchRequest> = TypedInternalResponse<ReqT, unknown>
export type UseFetchOptions< export interface UseFetchOptions<
DataT, DataT,
Transform extends _Transform<DataT, any> = _Transform<DataT, DataT>, Transform extends _Transform<DataT, any> = _Transform<DataT, DataT>,
PickKeys extends KeyOfRes<Transform> = KeyOfRes<Transform> PickKeys extends KeyOfRes<Transform> = KeyOfRes<Transform>
> = AsyncDataOptions<DataT, Transform, PickKeys> & FetchOptions & { key?: string } > extends
AsyncDataOptions<DataT, Transform, PickKeys>,
FetchOptions
{
key?: string
}
export function useFetch< export function useFetch<
ResT = void, ResT = void,
@ -32,15 +37,22 @@ export function useFetch<
return isRef(r) ? r.value : r return isRef(r) ? r.value : r
}) })
const asyncData = useAsyncData(key, () => { const _fetchOptions = {
return $fetch(_request.value, opts) as Promise<_ResT> ...opts,
}, { cache: typeof opts.cache === 'boolean' ? undefined : opts.cache
}
const _asyncDataOptions: AsyncDataOptions<any> = {
...opts, ...opts,
watch: [ watch: [
_request, _request,
...(opts.watch || []) ...(opts.watch || [])
] ]
}) }
const asyncData = useAsyncData(key, () => {
return $fetch(_request.value, _fetchOptions) as Promise<_ResT>
}, _asyncDataOptions)
return asyncData return asyncData
} }