SSR 和 SvelteKit
设置
SvelteKit 默认使用服务端渲染(SSR)来渲染路由。因此,你需要在服务端禁用查询。否则,你的查询将在服务端异步持续执行,即使 HTML 已发送至客户端。
推荐的做法是在 QueryClient 对象中使用 SvelteKit 的 browser 模块。这不会禁用 queryClient.query(),而 queryClient.query() 会在下文的某个解决方案中用到。
src/routes/+layout.svelte
svelte
1<script lang="ts">2 import { browser } from '$app/environment'3 import { QueryClient, QueryClientProvider } from '@tanstack/svelte-query'4 5 let { children } = $props()6 7 const queryClient = new QueryClient({8 defaultOptions: {9 queries: {10 enabled: browser,11 },12 },13 })14</script>15 16<QueryClientProvider client={queryClient}>17 {@render children()}18</QueryClientProvider>预取数据
Svelte Query 支持两种方式,在服务端预取数据,并通过 SvelteKit 将其传递给客户端。
若想查看理想的 SSR 设置,请参阅 SSR 示例。
使用 initialData
结合 SvelteKit 的 load,你可以将服务端加载的数据传入 createQuery 的 initialData 选项:
src/routes/+page.ts
TypeScript
1export async function load() {2 const posts = await getPosts()3 return { posts }4}src/routes/+page.svelte
svelte
1<script lang="ts">2 import { createQuery } from '@tanstack/svelte-query'3 import type { PageProps } from './$types'4 5 let { data }: PageProps = $props()6 7 const query = createQuery(() => ({8 queryKey: ['posts'],9 queryFn: getPosts,10 initialData: data.posts,11 }))12</script>优点:
- 此设置极简,对某些场景而言可快速落地
- 同时支持
+page.ts/+layout.ts和+page.server.ts/+layout.server.tsload 函数
缺点:
- 若你在组件树更深层调用
createQuery,则需将initialData逐层向下传递 - 若你在多个位置以相同查询调用
createQuery,则需向所有位置传递initialData - 无法获知查询在服务端的确切拉取时间,因此
dataUpdatedAt及判断查询是否需要重新拉取,均基于页面加载时间而非服务端拉取时间
使用 query
Svelte Query 支持在服务端预取查询。借助下方设置,你可在数据发送至用户浏览器前,将其拉取并注入 QueryClientProvider。因此,该数据已存在于缓存中,客户端首次渲染时无需执行初始拉取。
src/routes/+layout.ts
TypeScript
1import { browser } from '$app/environment'2import { QueryClient } from '@tanstack/svelte-query'3 4export async function load() {5 const queryClient = new QueryClient({6 defaultOptions: {7 queries: {8 enabled: browser,9 // With SSR, we usually want to set some default staleTime10 // above 0 to avoid refetching immediately on the client11 staleTime: 60 * 1000,12 },13 },14 })15 16 return { queryClient }17}src/routes/+layout.svelte
svelte
1<script lang="ts">2 import { QueryClientProvider } from '@tanstack/svelte-query'3 import type { LayoutProps } from './$types'4 5 let { data, children }: LayoutProps = $props()6</script>7 8<QueryClientProvider client={data.queryClient}>9 {@render children()}10</QueryClientProvider>src/routes/+page.ts
TypeScript
1import { noop } from '@tanstack/svelte-query'2 3export async function load({ parent, fetch }) {4 const { queryClient } = await parent()5 6 // You need to use the SvelteKit fetch function here7 await queryClient8 .query({9 queryKey: ['posts'],10 queryFn: async () => (await fetch('/api/posts')).json(),11 })12 .catch(noop)13}src/routes/+page.svelte
svelte
1<script lang="ts">2 import { createQuery } from '@tanstack/svelte-query'3 4 // This data is cached by query in +page.ts so no fetch actually happens here5 const query = createQuery(() => ({6 queryKey: ['posts'],7 queryFn: async () => (await fetch('/api/posts')).json(),8 }))9</script>优点:
- 服务端加载的数据可在任意位置访问,无需属性透传(prop-drilling)
- 页面渲染后,客户端不再执行初始拉取,因为查询缓存已完整保留该查询的所有信息,包括
dataUpdatedAt
缺点:
- 初始设置需更多文件
- 不适用于
+page.server.ts/+layout.server.tsload 函数(不过,与 TanStack Query 配合使用的 API 本身也需完全暴露给浏览器)