Skip to main content

SSR 和 SvelteKit

设置

SvelteKit 默认将在 SSR 编译阶段渲染路由。 出于这一点考虑,你需要在服务器端禁用查询,否则你的查询将在服务器端没有 HTML 内容的情况下异步运行(HTML 已被送至客户端)。

推荐的解决办法是在QueryClient实例中导入 SvelteKit 的browser模块。 这并不会禁用queryClient.prefetchQuery(),因为它涉及到如何预取数据:

src/routes/+layout.svelte

<script lang="ts">
import { browser } from '$app/environment'
import { QueryClient, QueryClientProvider } from '@tanstack/svelte-query'

const queryClient = new QueryClient({
defaultOptions: {
queries: {
enabled: browser,
},
},
})
</script>

<QueryClientProvider client={queryClient}>
<slot />
</QueryClientProvider>

预取数据

Svelte Query 支持两种将服务器端预取的数据传输到客户端的方法。

如果你希望有更为详尽的解释,希望有点代码可用,请查看 SSR 的入门例子.

使用 initialData

组合 SvelteKit 的 load特性,你可以将预取的数据在服务器端传输给createQueryinitialData选项:

src/routes/+page.ts

import type { PageLoad } from "./$types";

export const load: PageLoad = async () => {
const posts = await getPosts();
return { posts };
};

src/routes/+page.svelte

<script>
import { createQuery } from '@tanstack/svelte-query'
import type { PageData } from './$types'

export let data: PageData

const query = createQuery({
queryKey: ['posts'],
queryFn: getPosts,
initialData: data.posts
})
</script>

优点:

  • 这种方式是最小化的,且这是对于常用场景的最快的方法
  • +page.ts/+layout.ts以及+page.server.ts/+layout.server.ts的 load 函数无任何冲突

缺点:

  • 如果你在组件树的某个组件调用createQuery函数,你必须将initialData透传过去
  • 如果你通过createQuery创建的查询在多个地方被调用,你必须将initialData透传给每一个查询
  • 查询在服务器端预取的准确时间是无法获知的,所以dataUpdatedAt和其他关于重新获取的逻辑都依赖于页面何时被加载

使用 prefetchQuery

Svelte Query 支持在服务器端通过查询预取数据。 使用下面的设置,你可以早在数据送到客户端之前提前查询。 因此,这些数据已被标记为缓存对象,不能从客户端初始化:

src/routes/+layout.ts

import { browser } from "$app/environment";
import { QueryClient } from "@tanstack/svelte-query";
import type { LayoutLoad } from "./$types";

export const load: LayoutLoad = async () => {
const queryClient = new QueryClient({
defaultOptions: {
queries: {
enabled: browser,
},
},
});

return { queryClient };
};

src/routes/+layout.svelte

<script lang="ts">
import { QueryClientProvider } from '@tanstack/svelte-query'
import type { LayoutData } from './$types'

export let data: LayoutData
</script>

<QueryClientProvider client={data.queryClient}>
<slot />
</QueryClientProvider>

src/routes/+page.ts

import type { PageLoad } from "./$types";

export const load: PageLoad = async ({ parent, fetch }) => {
const { queryClient } = await parent();

// 你需要使用 SvelteKit 的 fetch 函数
await queryClient.prefetchQuery({
queryKey: ["posts"],
queryFn: async () => (await fetch("/api/posts")).json(),
});
};

src/routes/+page.svelte

<script lang="ts">
import { createQuery } from '@tanstack/svelte-query'

// 这个数据被 prefetchQuery 缓存在 +page.ts 里,所以(客户端)没有任何真实的查询被触发
const query = createQuery({
queryKey: ['posts'],
queryFn: async () => (await fetch('/api/posts')).json(),
})
</script>

优点:

  • 服务器端的数据不需要任何形式的透传,可以被任何地方随意访问
  • 在客户端页面渲染时没有初始化请求负载,因为缓存已经知悉了所有有关查询的信息,包括dataUpdatedAt

缺点:

  • 需要更为复杂的设置
  • 无法和 +page.server.ts/+layout.server.ts 的 load 函数一起使用 (并且,无论如何,与 Svelte Query 一起使用的 API 都需要完全暴露给浏览器)