Vue Query unterstützt das Vorabrufen mehrerer Abfragen auf dem Server und anschließend das Dehydrieren dieser Abfragen in die QueryClient. Das bedeutet, dass der Server Markup vorab rendern kann, das sofort beim Laden der Seite verfügbar ist, und sobald JS verfügbar ist, kann Vue Query diese Abfragen mit der vollen Funktionalität der Bibliothek aufrüsten oder hydrieren. Dies beinhaltet das erneute Abrufen dieser Abfragen auf dem Client, wenn sie seit dem Zeitpunkt ihres Renderings auf dem Server veraltet sind.
Erstellen Sie zunächst eine Datei vue-query.ts in Ihrem plugins-Verzeichnis mit dem folgenden Inhalt
import type {
DehydratedState,
VueQueryPluginOptions,
} from '@tanstack/vue-query'
import {
VueQueryPlugin,
QueryClient,
hydrate,
dehydrate,
} from '@tanstack/vue-query'
// Nuxt 3 app aliases
import { defineNuxtPlugin, useState } from '#imports'
export default defineNuxtPlugin((nuxt) => {
const vueQueryState = useState<DehydratedState | null>('vue-query')
// Modify your Vue Query global settings here
const queryClient = new QueryClient({
defaultOptions: { queries: { staleTime: 5000 } },
})
const options: VueQueryPluginOptions = { queryClient }
nuxt.vueApp.use(VueQueryPlugin, options)
if (import.meta.server) {
nuxt.hooks.hook('app:rendered', () => {
vueQueryState.value = dehydrate(queryClient)
})
}
if (import.meta.client) {
hydrate(queryClient, vueQueryState.value)
}
})
import type {
DehydratedState,
VueQueryPluginOptions,
} from '@tanstack/vue-query'
import {
VueQueryPlugin,
QueryClient,
hydrate,
dehydrate,
} from '@tanstack/vue-query'
// Nuxt 3 app aliases
import { defineNuxtPlugin, useState } from '#imports'
export default defineNuxtPlugin((nuxt) => {
const vueQueryState = useState<DehydratedState | null>('vue-query')
// Modify your Vue Query global settings here
const queryClient = new QueryClient({
defaultOptions: { queries: { staleTime: 5000 } },
})
const options: VueQueryPluginOptions = { queryClient }
nuxt.vueApp.use(VueQueryPlugin, options)
if (import.meta.server) {
nuxt.hooks.hook('app:rendered', () => {
vueQueryState.value = dehydrate(queryClient)
})
}
if (import.meta.client) {
hydrate(queryClient, vueQueryState.value)
}
})
Jetzt sind Sie bereit, Daten in Ihren Seiten mit onServerPrefetch vorab abzurufen.
export default defineComponent({
setup() {
const { data, suspense } = useQuery({
queryKey: ['test'],
queryFn: fetcher,
})
onServerPrefetch(async () => {
await suspense()
})
return { data }
},
})
export default defineComponent({
setup() {
const { data, suspense } = useQuery({
queryKey: ['test'],
queryFn: fetcher,
})
onServerPrefetch(async () => {
await suspense()
})
return { data }
},
})
Erstellen Sie zunächst eine Datei vue-query.js in Ihrem plugins-Verzeichnis mit dem folgenden Inhalt
import Vue from 'vue'
import { VueQueryPlugin, QueryClient, hydrate } from '@tanstack/vue-query'
export default (context) => {
// Modify your Vue Query global settings here
const queryClient = new QueryClient({
defaultOptions: { queries: { staleTime: 5000 } },
})
if (process.server) {
context.ssrContext.VueQuery = queryClient
}
if (process.client) {
Vue.use(VueQueryPlugin, { queryClient })
if (context.nuxtState && context.nuxtState.vueQueryState) {
hydrate(queryClient, context.nuxtState.vueQueryState)
}
}
}
import Vue from 'vue'
import { VueQueryPlugin, QueryClient, hydrate } from '@tanstack/vue-query'
export default (context) => {
// Modify your Vue Query global settings here
const queryClient = new QueryClient({
defaultOptions: { queries: { staleTime: 5000 } },
})
if (process.server) {
context.ssrContext.VueQuery = queryClient
}
if (process.client) {
Vue.use(VueQueryPlugin, { queryClient })
if (context.nuxtState && context.nuxtState.vueQueryState) {
hydrate(queryClient, context.nuxtState.vueQueryState)
}
}
}
Fügen Sie dieses Plugin zu Ihrer nuxt.config.js hinzu
module.exports = {
...
plugins: ['~/plugins/vue-query.js'],
}
module.exports = {
...
plugins: ['~/plugins/vue-query.js'],
}
Jetzt sind Sie bereit, Daten in Ihren Seiten mit onServerPrefetch vorab abzurufen.
// pages/todos.vue
<template>
<div>
<button @click="refetch">Refetch</button>
<p>{{ data }}</p>
</div>
</template>
<script lang="ts">
import {
defineComponent,
onServerPrefetch,
useContext,
} from '@nuxtjs/composition-api'
import { useQuery, useQueryClient, dehydrate } from '@tanstack/vue-query'
export default defineComponent({
setup() {
// Get QueryClient either from SSR context, or Vue context
const { ssrContext } = useContext()
// Make sure to provide `queryClient` as a second parameter to `useQuery` calls
const queryClient =
(ssrContext != null && ssrContext.VueQuery) || useQueryClient()
// This will be prefetched and sent from the server
const { data, refetch, suspense } = useQuery(
{
queryKey: ['todos'],
queryFn: getTodos,
},
queryClient,
)
// This won't be prefetched, it will start fetching on client side
const { data2 } = useQuery(
{
queryKey: 'todos2',
queryFn: getTodos,
},
queryClient,
)
onServerPrefetch(async () => {
await suspense()
ssrContext.nuxt.vueQueryState = dehydrate(queryClient)
})
return {
refetch,
data,
}
},
})
</script>
// pages/todos.vue
<template>
<div>
<button @click="refetch">Refetch</button>
<p>{{ data }}</p>
</div>
</template>
<script lang="ts">
import {
defineComponent,
onServerPrefetch,
useContext,
} from '@nuxtjs/composition-api'
import { useQuery, useQueryClient, dehydrate } from '@tanstack/vue-query'
export default defineComponent({
setup() {
// Get QueryClient either from SSR context, or Vue context
const { ssrContext } = useContext()
// Make sure to provide `queryClient` as a second parameter to `useQuery` calls
const queryClient =
(ssrContext != null && ssrContext.VueQuery) || useQueryClient()
// This will be prefetched and sent from the server
const { data, refetch, suspense } = useQuery(
{
queryKey: ['todos'],
queryFn: getTodos,
},
queryClient,
)
// This won't be prefetched, it will start fetching on client side
const { data2 } = useQuery(
{
queryKey: 'todos2',
queryFn: getTodos,
},
queryClient,
)
onServerPrefetch(async () => {
await suspense()
ssrContext.nuxt.vueQueryState = dehydrate(queryClient)
})
return {
refetch,
data,
}
},
})
</script>
Wie gezeigt, ist es in Ordnung, einige Abfragen vorab abzurufen und andere von der QueryClient abrufen zu lassen. Dies bedeutet, dass Sie steuern können, welche Inhalte der Server rendert oder nicht, indem Sie prefetchQuery oder suspense für eine bestimmte Abfrage hinzufügen oder entfernen.
Synchronisieren Sie den VueQuery-Client-Status mit vite-ssr, um ihn im DOM zu serialisieren
// main.js (entry point)
import App from './App.vue'
import viteSSR from 'vite-ssr/vue'
import {
QueryClient,
VueQueryPlugin,
hydrate,
dehydrate,
} from '@tanstack/vue-query'
export default viteSSR(App, { routes: [] }, ({ app, initialState }) => {
// -- This is Vite SSR main hook, which is called once per request
// Create a fresh VueQuery client
const queryClient = new QueryClient()
// Sync initialState with the client state
if (import.meta.env.SSR) {
// Indicate how to access and serialize VueQuery state during SSR
initialState.vueQueryState = { toJSON: () => dehydrate(queryClient) }
} else {
// Reuse the existing state in the browser
hydrate(queryClient, initialState.vueQueryState)
}
// Mount and provide the client to the app components
app.use(VueQueryPlugin, { queryClient })
})
// main.js (entry point)
import App from './App.vue'
import viteSSR from 'vite-ssr/vue'
import {
QueryClient,
VueQueryPlugin,
hydrate,
dehydrate,
} from '@tanstack/vue-query'
export default viteSSR(App, { routes: [] }, ({ app, initialState }) => {
// -- This is Vite SSR main hook, which is called once per request
// Create a fresh VueQuery client
const queryClient = new QueryClient()
// Sync initialState with the client state
if (import.meta.env.SSR) {
// Indicate how to access and serialize VueQuery state during SSR
initialState.vueQueryState = { toJSON: () => dehydrate(queryClient) }
} else {
// Reuse the existing state in the browser
hydrate(queryClient, initialState.vueQueryState)
}
// Mount and provide the client to the app components
app.use(VueQueryPlugin, { queryClient })
})
Rufen Sie dann VueQuery von jeder Komponente mit onServerPrefetch von Vue auf
<!-- MyComponent.vue -->
<template>
<div>
<button @click="refetch">Refetch</button>
<p>{{ data }}</p>
</div>
</template>
<script setup>
import { useQuery } from '@tanstack/vue-query'
import { onServerPrefetch } from 'vue'
// This will be prefetched and sent from the server
const { refetch, data, suspense } = useQuery({
queryKey: ['todos'],
queryFn: getTodos,
})
onServerPrefetch(suspense)
</script>
<!-- MyComponent.vue -->
<template>
<div>
<button @click="refetch">Refetch</button>
<p>{{ data }}</p>
</div>
</template>
<script setup>
import { useQuery } from '@tanstack/vue-query'
import { onServerPrefetch } from 'vue'
// This will be prefetched and sent from the server
const { refetch, data, suspense } = useQuery({
queryKey: ['todos'],
queryFn: getTodos,
})
onServerPrefetch(suspense)
</script>
Jede Abfrage mit einem Fehler wird automatisch von der Dehydrierung ausgeschlossen. Das bedeutet, dass das Standardverhalten darin besteht, so zu tun, als wären diese Abfragen nie auf dem Server geladen worden, normalerweise wird stattdessen ein Ladezustand angezeigt und die Abfragen werden auf der QueryClient erneut versucht. Dies geschieht unabhängig vom Fehler.
Manchmal ist dieses Verhalten unerwünscht, vielleicht möchten Sie stattdessen eine Fehlerseite mit einem korrekten Statuscode für bestimmte Fehler oder Abfragen rendern. In diesen Fällen verwenden Sie fetchQuery und fangen Sie Fehler ab, um diese manuell zu behandeln.
Eine Abfrage gilt als veraltet, abhängig davon, wann ihre dataUpdatedAt wurde. Ein Hinweis hierbei ist, dass der Server die korrekte Zeit haben muss, damit dies ordnungsgemäß funktioniert, aber die UTC-Zeit wird verwendet, daher spielen Zeitzonen hier keine Rolle.
Da staleTime standardmäßig auf 0 gesetzt ist, werden Abfragen standardmäßig im Hintergrund beim Laden der Seite erneut abgerufen. Möglicherweise möchten Sie eine höhere staleTime verwenden, um diesen doppelten Abruf zu vermeiden, insbesondere wenn Sie Ihren Markup nicht cachen.
Dieser erneute Abruf veralteter Abfragen passt perfekt, wenn Sie Markup in einem CDN cachen! Sie können die Cache-Zeit der Seite selbst recht hoch einstellen, um das erneute Rendern von Seiten auf dem Server zu vermeiden, aber die staleTime der Abfragen niedriger konfigurieren, um sicherzustellen, dass Daten im Hintergrund abgerufen werden, sobald ein Benutzer die Seite besucht. Vielleicht möchten Sie die Seiten für eine Woche cachen, aber die Daten beim Laden der Seite automatisch erneut abrufen, wenn sie älter als ein Tag sind?
Falls Sie die QueryClient für jede Anfrage erstellen, erstellt Vue Query den isolierten Cache für diesen Client, der für die Dauer von gcTime im Speicher erhalten bleibt. Dies kann zu einem hohen Speicherverbrauch auf dem Server führen, wenn während dieses Zeitraums eine hohe Anzahl von Anfragen eingeht.
Auf dem Server ist gcTime standardmäßig auf Infinity gesetzt, was die manuelle Speicherbereinigung deaktiviert und den Speicher automatisch löscht, sobald eine Anfrage abgeschlossen ist. Wenn Sie explizit eine nicht-unendliche gcTime festlegen, sind Sie für die frühzeitige Bereinigung des Caches verantwortlich.
Um den Cache zu leeren, sobald er nicht mehr benötigt wird, und den Speicherverbrauch zu senken, können Sie einen Aufruf an queryClient.clear() hinzufügen, nachdem die Anfrage bearbeitet und der dehydrierte Status an den Client gesendet wurde.
Alternativ können Sie eine kleinere gcTime festlegen.