diff --git a/examples/use-async-data/components/CounterExample.vue b/examples/use-async-data/components/CounterExample.vue
new file mode 100644
index 0000000000..7b4756d630
--- /dev/null
+++ b/examples/use-async-data/components/CounterExample.vue
@@ -0,0 +1,19 @@
+
+
+
+
+ {{ data }}
+
+
+ +
+
+
+ ⟳
+
+
+
+
diff --git a/examples/use-async-data/components/MountainExample.vue b/examples/use-async-data/components/MountainExample.vue
new file mode 100644
index 0000000000..9632969f99
--- /dev/null
+++ b/examples/use-async-data/components/MountainExample.vue
@@ -0,0 +1,9 @@
+
+
+
+ {{ mountain }}
+
diff --git a/packages/bridge/src/runtime/composables.ts b/packages/bridge/src/runtime/composables.ts
index 4fa9ef40f2..190511a6b5 100644
--- a/packages/bridge/src/runtime/composables.ts
+++ b/packages/bridge/src/runtime/composables.ts
@@ -8,7 +8,7 @@ import { sendRedirect } from 'h3'
import defu from 'defu'
import { useNuxtApp } from './app'
-export { useLazyAsyncData } from './asyncData'
+export { useLazyAsyncData, refreshNuxtData } from './asyncData'
export { useLazyFetch } from './fetch'
export { useCookie } from './cookie'
export { useRequestHeaders } from './ssr'
diff --git a/packages/nuxt3/src/app/composables/asyncData.ts b/packages/nuxt3/src/app/composables/asyncData.ts
index 38a4da5039..ae164d346e 100644
--- a/packages/nuxt3/src/app/composables/asyncData.ts
+++ b/packages/nuxt3/src/app/composables/asyncData.ts
@@ -137,12 +137,15 @@ export function useAsyncData<
asyncData.refresh()
}
if (options.watch) {
- const unwatch = watch(options.watch, () => {
- asyncData.refresh()
- })
- if (instance) {
- onUnmounted(() => unwatch())
+ watch(options.watch, () => asyncData.refresh())
+ }
+ const off = nuxt.hook('app:data:refresh', (keys) => {
+ if (!keys || keys.includes(key)) {
+ return asyncData.refresh()
}
+ })
+ if (instance) {
+ onUnmounted(off)
}
}
@@ -166,6 +169,14 @@ export function useLazyAsyncData<
return useAsyncData(key, handler, { ...options, lazy: true })
}
+export function refreshNuxtData (keys?: string | string[]): Promise
{
+ if (process.server) {
+ return Promise.resolve()
+ }
+ const _keys = keys ? Array.isArray(keys) ? keys : [keys] : undefined
+ return useNuxtApp().callHook('app:data:refresh', _keys)
+}
+
function pick (obj: Record, keys: string[]) {
const newObj = {}
for (const key of keys) {
diff --git a/packages/nuxt3/src/app/composables/index.ts b/packages/nuxt3/src/app/composables/index.ts
index 9f47d42c53..03300f5533 100644
--- a/packages/nuxt3/src/app/composables/index.ts
+++ b/packages/nuxt3/src/app/composables/index.ts
@@ -1,5 +1,5 @@
export { defineNuxtComponent } from './component'
-export { useAsyncData, useLazyAsyncData } from './asyncData'
+export { useAsyncData, useLazyAsyncData, refreshNuxtData } from './asyncData'
export type { AsyncDataOptions, AsyncData } from './asyncData'
export { useHydration } from './hydrate'
export { useState } from './state'
diff --git a/packages/nuxt3/src/app/nuxt.ts b/packages/nuxt3/src/app/nuxt.ts
index d3af122c2c..56b909b53b 100644
--- a/packages/nuxt3/src/app/nuxt.ts
+++ b/packages/nuxt3/src/app/nuxt.ts
@@ -23,6 +23,7 @@ export interface RuntimeNuxtHooks {
'app:suspense:resolve': (Component?: VNode) => HookResult
'app:error': (err: any) => HookResult
'app:error:cleared': (options: { redirect?: string }) => HookResult
+ 'app:data:refresh': (keys?: string[]) => HookResult
'page:start': (Component?: VNode) => HookResult
'page:finish': (Component?: VNode) => HookResult
'meta:register': (metaRenderers: Array<(nuxt: NuxtApp) => NuxtMeta | Promise>) => HookResult
diff --git a/packages/nuxt3/src/auto-imports/presets.ts b/packages/nuxt3/src/auto-imports/presets.ts
index 8c5c7bb53a..62e4a76af2 100644
--- a/packages/nuxt3/src/auto-imports/presets.ts
+++ b/packages/nuxt3/src/auto-imports/presets.ts
@@ -23,6 +23,7 @@ export const appPreset = defineUnimportPreset({
imports: [
'useAsyncData',
'useLazyAsyncData',
+ 'refreshNuxtData',
'defineNuxtComponent',
'useNuxtApp',
'defineNuxtPlugin',