news 2026/9/9 13:22:25

TanStack Query 在 SvelteKit 中如何用 load 函数预取数据并传给 QueryClientProvider?

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
TanStack Query 在 SvelteKit 中如何用 load 函数预取数据并传给 QueryClientProvider?

TanStack Query 在 SvelteKit 中如何用 load 函数预取数据并传给 QueryClientProvider?

【免费下载链接】query🤖 Powerful asynchronous state management, server-state utilities and data fetching for the web. TS/JS, React Query, Solid Query, Svelte Query and Vue Query.项目地址: https://gitcode.com/GitHub_Trending/qu/query

如果你的 SvelteKit 项目用@tanstack/svelte-query(Svelte Query)管理服务器状态,SSR 场景下最常见的诉求就是:页面 HTML 还没到浏览器之前,就在服务端把数据取好,随QueryClient一起交给客户端,组件挂载时不再发起第一次请求。官方文档 SSR and SvelteKit 给出了两种方式,本文的主路径是其中更适合 SvelteKit 的方式——在服务端load函数里用queryClient.query()预取数据,把整个QueryClientload传给QueryClientProvider。参考实现可直接对照仓库中的 SSR 示例,该示例基于@tanstack/svelte-query^6.1.48@sveltejs/kit^2.57.1、Svelte 5。

先决设置:在服务端禁用 query 的自动执行

SvelteKit 默认以 SSR 方式渲染路由。文档明确要求:如果不关闭服务端执行,查询会在 HTML 已经发给客户端之后继续在服务器上异步执行。推荐做法是在QueryClientdefaultOptions中使用$app/environment导出的browser模块:

const queryClient = new QueryClient({ defaultOptions: { queries: { enabled: browser, }, }, })

enabled: browser只影响createQuery等 hook 的行为;文档特别指出,它不会禁用queryClient.query(),而下面的服务端预取正是靠后者完成。

第一步:在 +layout.ts 的 load 中创建并返回 QueryClient

在根布局的 load 函数里新建QueryClient,作为 load 的返回值的一部分交给子路由和页面。官方示例的+layout.ts如下(示例源码):

import { QueryClient } from '@tanstack/svelte-query' import { browser } from '$app/environment' import type { LayoutLoad } from './$types' export const load: LayoutLoad = () => { const queryClient = new QueryClient({ defaultOptions: { queries: { enabled: browser, staleTime: 60 * 1000, }, }, }) return { queryClient } }

文档中的版本没有staleTime,官方示例额外配置了staleTime: 60 * 1000,按需保留即可。关键点只有一个:QueryClient实例必须在服务端load中创建并通过返回值向下游传递,页面级load才能拿到同一个实例往里面塞数据。

第二步:在 +layout.svelte 中用 QueryClientProvider 包一层

在根布局组件里从data.queryClient取出上一步的实例,传给QueryClientProvider(文档给出的+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>

示例项目运行在 Svelte 5 runes 模式下,写法等价,改为const { data, children } = $props()并渲染{@render children()}。示例还在 provider 内挂载了<SvelteQueryDevtools />(来自@tanstack/svelte-query-devtools),用于查看缓存状态,属于可选项,不参与预取流程。

注意版本差异:文档示例使用export let data: LayoutData(Svelte 4 风格),示例项目使用$props()(Svelte 5 runes),两者二选一,与项目 Svelte 版本保持一致即可。

第三步:在页面 load 中用 queryClient.query 预取数据

页面级+page.ts通过parent()拿到布局传来的queryClient,调用queryClient.query()完成服务端预取。文档给出的代码:

export async function load({ parent, fetch }) { const { queryClient } = await parent() // You need to use the SvelteKit fetch function here await queryClient .query({ queryKey: ['posts'], queryFn: async () => (await fetch('/api/posts')).json(), }) .catch(noop) }

两个必须注意的点:

  • 文档原样注释了queryFn 里必须使用 SvelteKit 的fetch(即load函数的fetch参数),不能直接调globalThis.fetch
  • 调用后统一接.catch(noop)noop@tanstack/svelte-query导入,官方示例 同样如此:
import { noop } from '@tanstack/svelte-query' import type { PageLoad } from './$types' import { api } from '$lib/api' export const load: PageLoad = async ({ parent, fetch }) => { const { queryClient } = await parent() await queryClient .query({ queryKey: ['posts', 10], queryFn: () => api(fetch).getPosts(10), }) .catch(noop) }

示例里api(fetch)只是把 SvelteKit 的fetch注入到一个请求封装中(api.ts),本质仍是“queryFn 使用 SvelteKit 提供的 fetch”。

第四步:组件用相同 queryKey 调用 createQuery

预取完成后,页面组件用与服务端query调用相同的queryKey创建查询。文档给出的+page.svelte

<script lang="ts"> import { createQuery } from '@tanstack/svelte-query' // This data is cached by query in +page.ts so no fetch actually happens here const query = createQuery(() => ({ queryKey: ['posts'], queryFn: async () => (await fetch('/api/posts')).json(), })) </script>

由于+page.ts里的queryClient.query()已经把数据写进了缓存,客户端组件挂载时命中缓存,文档结论是:页面渲染后不会发生初次 fetch,且 query 缓存保留了dataUpdatedAt等完整信息。对照示例,Posts.svelte组件使用与页面 load 一致的queryKey: ['posts', limit]创建查询,并通过client.getQueryData(['post', post.id])判断某条数据是否已缓存(Posts.svelte)。

如何验证预取生效

按示例项目的方式启动验证(examples/svelte/ssr):

npm run dev

(示例package.jsondev脚本为vite dev,也可用npm run preview预览npm run build的构建产物。)

判断依据以文档描述为准:

  1. 页面首屏数据来自服务端预取的缓存,客户端不产生该 query 的初次请求;
  2. 组件内posts.status不会停留在'pending'(示例组件按pending/error/正常三态渲染列表);
  3. 若挂载了SvelteQueryDevtools,可在开发者工具面板中查看该 query 已处于缓存状态。

限制与注意事项

  • 文档明确说明:queryClient.query()这套方案不能用于+page.server.ts/+layout.server.ts的 load 函数(文档解释为:配合 TanStack Query 使用的 API 本就需要对浏览器完全暴露)。如果你的数据必须放在 server-only 的 load 里,只能走下面的initialData替代方案。
  • 服务端预取的 query 与客户端createQuery必须使用相同的queryKey,否则缓存无法命中,客户端仍会发起请求。
  • 服务端queryFn里务必使用load传入的 SvelteKitfetch,这是文档的显式要求。

替代方案:用 initialData 直接传数据

文档同时提供了更轻量的方式:页面load里请求数据并以return { posts }返回,组件把它传给createQueryinitialData

// src/routes/+page.ts export async function load() { const posts = await getPosts() return { posts } }
<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传下去;同一查询在多处使用时每处都要传;且无法知道数据在服务端何时取回,dataUpdatedAt与是否需要 refetch 的判断基于页面加载时间而非服务端取数时间。

如果预取目标是“数据进入 QueryClient 缓存、全局任意组件免 prop-drilling 可用”,选本文主路径的queryClient.query()方案;如果数据只被单页使用且逻辑简单,initialData更省事。完整可运行代码见 examples/svelte/ssr,更多背景见 SSR and SvelteKit 文档。

【免费下载链接】query🤖 Powerful asynchronous state management, server-state utilities and data fetching for the web. TS/JS, React Query, Solid Query, Svelte Query and Vue Query.项目地址: https://gitcode.com/GitHub_Trending/qu/query

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/9 13:22:05

基于S7-1200的升降横移式立体车库PLC控制方案

电梯房小区、医院停车场、老旧小区改造&#xff0c;这几年立体停车库的需求是真的大。我前前后后参与过几个升降横移式立体车库的项目&#xff0c;其中用西门子S7-1200系列做主控的方案占了大多数。说实话&#xff0c;立体车库在自动化项目里属于“麻雀虽小五脏俱全”的类型&am…

作者头像 李华
网站建设 2026/9/9 13:20:34

TSA技术原理与实验流程:从信号放大到多重免疫组化

干了这么多年病理与免疫组化实验&#xff0c;TSA&#xff08;Tyramide Signal Amplification&#xff0c;酪胺信号放大&#xff09;是我这几年用下来觉得“上限最高、门槛也最明显”的一项技术。很多人一开始听说TSA&#xff0c;是因为它能把微弱到几乎看不见的阳性信号放大到一…

作者头像 李华
网站建设 2026/9/9 13:18:36

数据结构C语言版补考速成:核心考点与代码模板

数据结构&#xff08;C语言版&#xff09;这门课&#xff0c;几乎是计算机专业的第一道分水岭。期末前很多人手里只剩一本教材和一堆没整理完的笔记&#xff0c;补考前想临时抱佛脚&#xff0c;结果连单链表反转都要对着代码发半天呆。这篇文章不讨论“数据结构重不重要”&…

作者头像 李华
网站建设 2026/9/9 13:16:50

SpringBoot QPS监控实战:从自研滑动窗口到Prometheus+Grafana告警

说实话&#xff0c;我以前也觉得QPS监控是那种“大团队才需要搞的东西”&#xff0c;小项目压测一下没问题直接上线就行了。直到上个月我们一个订单查询接口在下午高峰期突然雪崩&#xff0c;从用户开始反馈到系统完全不可用&#xff0c;前后也就十来分钟。事后复盘的时候发现&…

作者头像 李华