diff --git a/docs/3.api/1.composables/use-async-data.md b/docs/3.api/1.composables/use-async-data.md index 866705e69a..ff318a552f 100644 --- a/docs/3.api/1.composables/use-async-data.md +++ b/docs/3.api/1.composables/use-async-data.md @@ -26,6 +26,7 @@ type AsyncDataOptions = { server?: boolean lazy?: boolean immediate?: boolean + deep?: boolean default?: () => DataT | Ref | null transform?: (input: DataT) => DataT pick?: string[] @@ -60,6 +61,7 @@ type AsyncDataRequestStatus = 'idle' | 'pending' | 'success' | 'error' * _transform_: a function that can be used to alter `handler` function result after resolving * _pick_: only pick specified keys in this array from the `handler` function result * _watch_: watch reactive sources to auto-refresh + * _deep_: return data in a deep ref object (it is `true` by default). It can be set to `false` to return data in a shallow ref object, which can improve performance if your data does not need to be deeply reactive. Under the hood, `lazy: false` uses `` 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. diff --git a/docs/3.api/1.composables/use-fetch.md b/docs/3.api/1.composables/use-fetch.md index b3bdc3cd11..765bee2c53 100644 --- a/docs/3.api/1.composables/use-fetch.md +++ b/docs/3.api/1.composables/use-fetch.md @@ -26,6 +26,7 @@ type UseFetchOptions = { server?: boolean lazy?: boolean immediate?: boolean + deep?: boolean default?: () => DataT transform?: (input: DataT) => DataT pick?: string[] @@ -72,6 +73,7 @@ All fetch options can be given a `computed` or `ref` value. These will be watche * `transform`: a function that can be used to alter `handler` function result after resolving * `pick`: only pick specified keys in this array from the `handler` function result * `watch`: watch an array of reactive sources and auto-refresh the fetch result when they change. Fetch options and URL are watched by default. You can completely ignore reactive sources by using `watch: false`. Together with `immediate: false`, this allows for a fully-manual `useFetch`. + * `deep`: return data in a deep ref object (it is `true` by default). It can be set to `false` to return data in a shallow ref object, which can improve performance if your data does not need to be deeply reactive. ::alert{type=warning} If you provide a function or ref as the `url` parameter, or if you provide functions as arguments to the `options` parameter, then the [`useFetch`](/docs/api/composables/use-fetch) call will not match other [`useFetch`](/docs/api/composables/use-fetch) calls elsewhere in your codebase, even if the options seem to be identical. If you wish to force a match, you may provide your own key in `options`. diff --git a/docs/3.api/1.composables/use-state.md b/docs/3.api/1.composables/use-state.md index e33472ca05..9c82f7af60 100644 --- a/docs/3.api/1.composables/use-state.md +++ b/docs/3.api/1.composables/use-state.md @@ -24,3 +24,12 @@ Because the data inside [`useState`](/docs/api/composables/use-state) will be se ::ReadMore{link="/docs/getting-started/state-management"} :: + +## Using `shallowRef` + +If you don't need your state to be deeply reactive, you can combine `useState` with [`shallowRef`](https://vuejs.org/api/reactivity-advanced.html#shallowref). This can improve performance when your state contains large objects and arrays. + +```ts +const state = useState('my-shallow-state', () => shallowRef({ deep: 'not reactive' })) +// isShallow(state) === true +``` diff --git a/packages/nuxt/src/app/composables/asyncData.ts b/packages/nuxt/src/app/composables/asyncData.ts index 62cae6ecd9..25d39dc49d 100644 --- a/packages/nuxt/src/app/composables/asyncData.ts +++ b/packages/nuxt/src/app/composables/asyncData.ts @@ -1,4 +1,4 @@ -import { getCurrentInstance, onBeforeMount, onServerPrefetch, onUnmounted, ref, toRef, unref, watch } from 'vue' +import { getCurrentInstance, onBeforeMount, onServerPrefetch, onUnmounted, ref, shallowRef, toRef, unref, watch } from 'vue' import type { Ref, WatchSource } from 'vue' import type { NuxtApp } from '../nuxt' import { useNuxtApp } from '../nuxt' @@ -44,6 +44,7 @@ export interface AsyncDataOptions< pick?: PickKeys watch?: MultiWatchSources immediate?: boolean + deep?: boolean } export interface AsyncDataExecuteOptions { @@ -148,8 +149,10 @@ export function useAsyncData< if (!nuxt._asyncData[key] || !options.immediate) { nuxt.payload._errors[key] ??= null + const _ref = options.deep !== true ? shallowRef : ref + nuxt._asyncData[key] = { - data: ref(getCachedData() ?? options.default!()), + data: _ref(getCachedData() ?? options.default!()), pending: ref(!hasCachedData()), error: toRef(nuxt.payload._errors, key), status: ref('idle') diff --git a/packages/nuxt/src/app/composables/fetch.ts b/packages/nuxt/src/app/composables/fetch.ts index a3cb38fc51..ed97a9af2e 100644 --- a/packages/nuxt/src/app/composables/fetch.ts +++ b/packages/nuxt/src/app/composables/fetch.ts @@ -107,6 +107,7 @@ export function useFetch< pick, watch, immediate, + deep, ...fetchOptions } = opts @@ -122,6 +123,7 @@ export function useFetch< transform, pick, immediate, + deep, watch: watch === false ? [] : [_fetchOptions, _request, ...(watch || [])] }