perf(nuxt): avoid multiple calls to getCachedData (#28472)

This commit is contained in:
Martin André 2024-08-13 21:24:32 +02:00 committed by Daniel Roe
parent 9397def672
commit b13582205a
No known key found for this signature in database
GPG Key ID: CBC814C393D93268
2 changed files with 22 additions and 9 deletions

View File

@ -252,18 +252,17 @@ export function useAsyncData<
console.warn('[nuxt] `boolean` values are deprecated for the `dedupe` option of `useAsyncData` and will be removed in the future. Use \'cancel\' or \'defer\' instead.') console.warn('[nuxt] `boolean` values are deprecated for the `dedupe` option of `useAsyncData` and will be removed in the future. Use \'cancel\' or \'defer\' instead.')
} }
// TODO: make more precise when v4 lands
const hasCachedData = () => options.getCachedData!(key, nuxtApp) != null
// Create or use a shared asyncData entity // Create or use a shared asyncData entity
const initialCachedData = options.getCachedData!(key, nuxtApp)
const hasCachedData = initialCachedData != null
if (!nuxtApp._asyncData[key] || !options.immediate) { if (!nuxtApp._asyncData[key] || !options.immediate) {
nuxtApp.payload._errors[key] ??= asyncDataDefaults.errorValue nuxtApp.payload._errors[key] ??= asyncDataDefaults.errorValue
const _ref = options.deep ? ref : shallowRef const _ref = options.deep ? ref : shallowRef
nuxtApp._asyncData[key] = { nuxtApp._asyncData[key] = {
data: _ref(options.getCachedData!(key, nuxtApp) ?? options.default!()), data: _ref(hasCachedData ? initialCachedData : options.default!()),
pending: ref(!hasCachedData()), pending: ref(!hasCachedData),
error: toRef(nuxtApp.payload._errors, key), error: toRef(nuxtApp.payload._errors, key),
status: ref('idle'), status: ref('idle'),
_default: options.default!, _default: options.default!,
@ -285,8 +284,11 @@ export function useAsyncData<
(nuxtApp._asyncDataPromises[key] as any).cancelled = true (nuxtApp._asyncDataPromises[key] as any).cancelled = true
} }
// Avoid fetching same key that is already fetched // Avoid fetching same key that is already fetched
if ((opts._initial || (nuxtApp.isHydrating && opts._initial !== false)) && hasCachedData()) { if ((opts._initial || (nuxtApp.isHydrating && opts._initial !== false))) {
return Promise.resolve(options.getCachedData!(key, nuxtApp)) const cachedData = opts._initial ? initialCachedData : options.getCachedData!(key, nuxtApp)
if (cachedData != null) {
return Promise.resolve(cachedData)
}
} }
asyncData.pending.value = true asyncData.pending.value = true
asyncData.status.value = 'pending' asyncData.status.value = 'pending'
@ -375,7 +377,7 @@ export function useAsyncData<
onUnmounted(() => cbs.splice(0, cbs.length)) onUnmounted(() => cbs.splice(0, cbs.length))
} }
if (fetchOnServer && nuxtApp.isHydrating && (asyncData.error.value || hasCachedData())) { if (fetchOnServer && nuxtApp.isHydrating && (asyncData.error.value || initialCachedData != null)) {
// 1. Hydration (server: true): no fetch // 1. Hydration (server: true): no fetch
asyncData.pending.value = false asyncData.pending.value = false
asyncData.status.value = asyncData.error.value ? 'error' : 'success' asyncData.status.value = asyncData.error.value ? 'error' : 'success'

View File

@ -227,6 +227,17 @@ describe('useAsyncData', () => {
`) `)
}) })
it('should only call getCachedData once', async () => {
const getCachedData = vi.fn(() => ({ val: false }))
const { data } = await useAsyncData(() => Promise.resolve({ val: true }), { getCachedData })
expect(data.value).toMatchInlineSnapshot(`
{
"val": false,
}
`)
expect(getCachedData).toHaveBeenCalledTimes(1)
})
it('should use default while pending', async () => { it('should use default while pending', async () => {
const promise = useAsyncData(() => Promise.resolve('test'), { default: () => 'default' }) const promise = useAsyncData(() => Promise.resolve('test'), { default: () => 'default' })
const { data, pending } = promise const { data, pending } = promise