diff --git a/packages/nuxt3/src/app/composables/fetch.ts b/packages/nuxt3/src/app/composables/asyncData.ts similarity index 58% rename from packages/nuxt3/src/app/composables/fetch.ts rename to packages/nuxt3/src/app/composables/asyncData.ts index e537763f52..7bcb2ac61f 100644 --- a/packages/nuxt3/src/app/composables/fetch.ts +++ b/packages/nuxt3/src/app/composables/asyncData.ts @@ -1,25 +1,22 @@ -import { Ref, toRef, onMounted, watch, getCurrentInstance, onUnmounted } from 'vue' +import { Ref, ref, toRef, onMounted, watch, getCurrentInstance, onUnmounted } from 'vue' import { Nuxt, useNuxt } from 'nuxt/app' -import { $fetch } from 'ohmyfetch' import { useData } from './data' -export type HTTPRequest = string | { method: string, url: string } -export type FetchRequest = HTTPRequest | ((ctx: Nuxt) => HTTPRequest | Promise) +export type AsyncDataFn = (ctx?: Nuxt) => Promise -export interface FetchOptions { +export interface AsyncDataOptions { server?: boolean defer?: boolean - fetcher?: Function - key?: string } -export interface FetchObj { +export interface AsyncDataObj { data: Ref - fetch: Function + pending: Ref + refresh: Function error?: any } -export function useFetch (defaults?: FetchOptions) { +export function useAsyncData (defaults?: AsyncDataOptions) { const nuxt = useNuxt() const vm = getCurrentInstance() @@ -40,31 +37,32 @@ export function useFetch (defaults?: FetchOptions) { }) } - return async function fetch (request: FetchRequest, options?: FetchOptions): Promise> { + return async function asyncData (handler: AsyncDataFn, options?: AsyncDataOptions): Promise> { + if (typeof handler !== 'function') { + throw new TypeError('asyncData handler must be a function') + } options = { server: true, defer: false, - fetcher: globalThis.$fetch || $fetch, ...defaults, ...options } const key = String(dataRef++) + const pending = ref(true) const fetch = async () => { - const _request = typeof request === 'function' - ? request(nuxt) - : request + pending.value = true + const _handler = handler(nuxt) - if (_request instanceof Promise) { + if (_handler instanceof Promise) { // Let user resolve if request is promise - data[key] = await _request - } else if (_request && (typeof _request === 'string' || _request.url)) { - // Make HTTP request when request is string (url) or { url, ...opts } - data[key] = await options.fetcher(_request) + // TODO: handle error + data[key] = await _handler + pending.value = false } else { // Invalid request - throw new Error('Invalid fetch request: ' + _request) + throw new TypeError('Invalid asyncData handler: ' + _handler) } } @@ -73,6 +71,9 @@ export function useFetch (defaults?: FetchOptions) { // Client side if (process.client) { // 1. Hydration (server: true): no fetch + if (nuxt.isHydrating && options.server) { + pending.value = false + } // 2. Initial load (server: false): fetch on mounted if (nuxt.isHydrating && !options.server) { // Fetch on mounted (initial load or deferred fetch) @@ -86,10 +87,8 @@ export function useFetch (defaults?: FetchOptions) { await fetch() } } - // Watch request - if (typeof request === 'function') { - watch(request, fetch) - } + // Watch handler + watch(handler.bind(null, nuxt), fetch) } // Server side @@ -99,7 +98,8 @@ export function useFetch (defaults?: FetchOptions) { return { data: toRef(data, key), - fetch + pending, + refresh: fetch } } } diff --git a/packages/nuxt3/src/app/composables/index.ts b/packages/nuxt3/src/app/composables/index.ts index dcb7ab89f3..3e5bf34e6c 100644 --- a/packages/nuxt3/src/app/composables/index.ts +++ b/packages/nuxt3/src/app/composables/index.ts @@ -1,3 +1,3 @@ -export { useFetch } from './fetch' +export { useAsyncData } from './asyncData' export { useData } from './data' export { useHydration } from './hydrate' diff --git a/packages/nuxt3/src/app/nuxt/index.ts b/packages/nuxt3/src/app/nuxt/index.ts index 7a36f3ab46..c83440dca3 100644 --- a/packages/nuxt3/src/app/nuxt/index.ts +++ b/packages/nuxt3/src/app/nuxt/index.ts @@ -51,11 +51,13 @@ export function createNuxt (options: CreateOptions) { nuxt.provide = (name: string, value: any) => { const $name = '$' + name - defineGetter(nuxt.app, $name, value) + defineGetter(nuxt, $name, value) defineGetter(nuxt.app.config.globalProperties, $name, value) } - nuxt.provide('nuxt', nuxt) + // Inject $nuxt + defineGetter(nuxt.app, '$nuxt', nuxt) + defineGetter(nuxt.app.config.globalProperties, '$nuxt', nuxt) // Expose nuxt to the renderContext if (nuxt.ssrContext) {