diff --git a/src/fetch.ts b/src/fetch.ts index 1ed1f524..5097c251 100644 --- a/src/fetch.ts +++ b/src/fetch.ts @@ -1,5 +1,9 @@ import Vue from 'vue' -import { getCurrentInstance, onBeforeMount } from '@vue/composition-api' +import { + getCurrentInstance, + onBeforeMount, + onServerPrefetch, +} from '@vue/composition-api' import { globalContext, globalNuxt, isFullStatic } from './globals' import type { NuxtApp } from '@nuxt/types/app' @@ -97,6 +101,16 @@ async function callFetches(this: AugmentedComponentInstance) { this.$nextTick(() => (this[globalNuxt] as any).nbFetching--) } +const setFetchState = (vm: AugmentedComponentInstance) => { + vm.$fetchState = + vm.$fetchState || + Vue.observable({ + error: null, + pending: false, + timestamp: 0, + }) +} + const loadFullStatic = (vm: AugmentedComponentInstance) => { // Check if component has been fetched on server const { fetchOnServer } = vm.$options @@ -126,6 +140,35 @@ const loadFullStatic = (vm: AugmentedComponentInstance) => { } } +async function serverPrefetch(vm: AugmentedComponentInstance) { + if (!vm._fetchOnServer) return + + // Call and await on $fetch + setFetchState(vm) + + try { + await callFetches.call(vm) + } catch (err) { + vm.$fetchState.error = normalizeError(err) + } + vm.$fetchState.pending = false + + // Define an ssrKey for hydration + vm._fetchKey = vm.$ssrContext.nuxt.fetch.length + + // Add data-fetch-key on parent element of Component + if (!vm.$vnode.data) vm.$vnode.data = {} + const attrs = (vm.$vnode.data.attrs = vm.$vnode.data.attrs || {}) + attrs['data-fetch-key'] = vm._fetchKey + + // Add to ssrContext for window.__NUXT__.fetch + vm.$ssrContext.nuxt.fetch.push( + vm.$fetchState.error + ? { _error: vm.$fetchState.error } + : JSON.parse(JSON.stringify(vm._data)) + ) +} + /** * Versions of Nuxt newer than v2.12 support a [custom hook called `fetch`](https://nuxtjs.org/api/pages-fetch/) that allows server-side and client-side asynchronous data-fetching. @@ -161,11 +204,16 @@ export const useFetch = (callback: Fetch) => { registerCallback(vm, callback) - if (process.server) { - vm.$options.fetch = callFetches.bind(vm) - return + if (typeof vm.$options.fetchOnServer === 'function') { + vm._fetchOnServer = vm.$options.fetchOnServer.call(vm) !== false + } else { + vm._fetchOnServer = vm.$options.fetchOnServer !== false } + setFetchState(vm) + + onServerPrefetch(() => serverPrefetch(vm)) + function result() { return { fetch: vm!.$fetch, @@ -178,19 +226,11 @@ export const useFetch = (callback: Fetch) => { vm._fetchDelay = typeof vm.$options.fetchDelay === 'number' ? vm.$options.fetchDelay : 200 - vm.$fetchState = - vm.$fetchState || - Vue.observable({ - error: null, - pending: false, - timestamp: 0, - }) - vm.$fetch = callFetches.bind(vm) onBeforeMount(() => !vm._hydrated && callFetches.call(vm)) - if (!isSsrHydration(vm)) { + if (process.server || !isSsrHydration(vm)) { if (isFullStatic) onBeforeMount(() => loadFullStatic(vm)) return result() } diff --git a/src/index.ts b/src/index.ts index 3470d91f..2c87f1e3 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,7 +4,7 @@ import { rmdirSync, readdirSync, copyFileSync, existsSync, mkdirSync } from 'fs' import type { Module } from '@nuxt/types' // eslint-disable-next-line -const { isFullStatic } = require('@nuxt/utils') +const utils = require('@nuxt/utils') const compositionApiModule: Module = function () { const libRoot = resolve(__dirname, '..') @@ -55,7 +55,7 @@ const compositionApiModule: Module = function () { src: resolve(libRoot, 'lib', 'entrypoint.js'), fileName: join('composition-api', 'index.js'), options: { - isFullStatic: isFullStatic(this.options), + isFullStatic: 'isFullStatic' in utils && utils.isFullStatic(this.options), staticPath, publicPath: join(this.options.router?.base || '', '/'), globalContext,