资讯动态

如何用 Lit SSR 与 TanStack Query 实现服务端预取及客户端 hydrate 水合?

发布时间:2026/9/10 4:45:47 来源:尧图企业网站定制
如何用 Lit SSR 与 TanStack Query 实现服务端预取及客户端 hydrate 水合【免费下载链接】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如果你的 Lit 应用需要首屏由服务端直接带数据渲染而不是等浏览器发请求后转圈同时希望客户端接管时能无缝复用这份数据就需要把 Lit SSR 和 TanStack Query 的缓存水合机制组合起来。官方指南 Server Rendering Hydration 给出的做法是用tanstack/lit-query从 TanStack Query Core 再导出的dehydrate/hydrateAPI在服务端预取查询、把缓存序列化进 HTML再在浏览器里水合到一个新的QueryClient。本文所有步骤都能对照仓库里的可运行示例 examples/lit/ssr 执行。适用前提Lit 2.8 及以上含 Lit 3Node 服务端以及 TanStack Query 支持的现代浏览器Chrome 91、Firefox 90、Edge 91、Safari 15、iOS 15、Opera 77。准备条件依赖安装与环境要求按 Lit Query 安装文档先安装适配器、核心包和 Litnpm i tanstack/lit-query tanstack/query-core litpnpm、yarn、bun 有对应的等价命令。注意tanstack/query-core是tanstack/lit-query的 peer dependency即使代码里都是从tanstack/lit-query导入 API也应该显式安装tanstack/query-core。SSR 场景还需要 Lit 官方的服务端渲染包。示例 examples/lit/ssr/package.json 中的实际版本组合是{ dependencies: { lit-labs/ssr: ^3.3.0, tanstack/lit-query: ^0.2.20, tanstack/query-core: ^5.102.8, lit: ^3.3.1 }, devDependencies: { lit-labs/ssr-client: ^1.1.7 } }其中lit-labs/ssr用于服务端渲染lit-labs/ssr-client提供客户端 hydrate 支持。安装文档同时标注Lit 适配器仍处于实验阶段API 尚未稳定需要更强版本稳定性时应锁定精确版本。整体流程服务端渲染的三个阶段指南把整个流程分为三步后文按这个顺序展开为每个请求创建一个独立的QueryClient。在服务端用这个 client 预取查询并用它渲染 Lit HTML。把缓存dehydrate进 HTML在浏览器渲染前hydrate到一个新的浏览器QueryClient。指南强调一条硬约束不要在不同用户或不同请求之间共享同一个服务端QueryClient。示例的 服务端代码 每次请求都在renderPage内部新建 client遵循的就是这条规则。服务端预取查询并渲染 HTML指南给出的服务端最小写法如下来自 ssr.mdimport { render } from lit-labs/ssr import { collectResult } from lit-labs/ssr/lib/render-result.js import { html } from lit import { QueryClient, dehydrate, noop } from tanstack/lit-query import { createDataQueryOptions } from ./api.js import ./app.js async function renderPage() { const apiBaseUrl https://example.com const queryClient new QueryClient({ defaultOptions: { queries: { staleTime: 30_000, }, }, }) await queryClient.query(createDataQueryOptions(apiBaseUrl)).catch(noop) const appHtml await collectResult( render( htmlssr-app api-base-url${apiBaseUrl} .queryClient${queryClient} /ssr-app, ), ) const dehydratedState dehydrate(queryClient) return { appHtml, dehydratedState } }两个关键点服务端通过属性绑定.queryClient${queryClient}把同一个 client 传给 Lit 元素这样createQueryController在服务端渲染阶段就能直接读到预取好的缓存。如果queryFn里会调用fetch要传绝对路径的 API origin上面代码中的https://example.com是文档示例值需替换为你的实际 API 地址不能依赖浏览器相对 URL。可运行示例的 server/index.mjs 在此基础上更严格它用queryClient.prefetchQuery(...)预取后检查queryClient.getQueryState(DATA_QUERY_KEY)的status不是success就直接抛错拒绝渲染 loading HTMLconst prefetchedQueryState queryClient.getQueryState(DATA_QUERY_KEY) if (prefetchedQueryState?.status ! success) { throw new Error( SSR prefetch did not complete successfully. Refusing to render loading HTML., ) }把 dehydrated 状态序列化进 HTMLdehydrate(queryClient)得到的状态需要以 JSON 形式嵌入 HTML并且要转义可能逃逸出script标签的字符。示例服务端用的序列化函数会把、、以及 U2028/U2029 转成\uXXXX形式见 server/index.mjs 的serializeJsonForHtml然后替换构建产物模板中的占位符const htmlDocument template .replace(__SSR_APP_HTML__, appHtml) .replace(__QUERY_STATE_JSON__, serializeJsonForHtml(dehydratedState))模板是 examples/lit/ssr/index.htmlVite 构建后产物位于dist/index.html占位符位置如下body __SSR_APP_HTML__ script id__QUERY_STATE__ typeapplication/json __QUERY_STATE_JSON__ /script script typemodule src/src/main.ts/script /body其中__SSR_APP_HTML__由服务端替换为渲染出的ssr-appHTML__QUERY_STATE_JSON__替换为转义后的缓存 JSON客户端水合时会从id__QUERY_STATE__的 script 里读取。客户端水合后再接管应用客户端入口 src/main.ts 的顺序是固定的先导入 hydrate 支持模块创建新的QueryClient并mount()读取并水合 dehydrated 状态把 client 赋给服务端渲染出来的元素最后才动态导入组件让元素升级——这样组件升级时预取缓存已经可用import lit-labs/ssr-client/lit-element-hydrate-support.js import { QueryClient, hydrate, type DehydratedState } from tanstack/lit-query import { QUERY_STALE_TIME } from ./api.js const appElement document.querySelector(ssr-app) as HTMLElement { queryClient?: QueryClient } | null if (!appElement) { throw new Error(Expected the SSR app element to exist before hydration.) } const queryClient new QueryClient({ defaultOptions: { queries: { retry: false, staleTime: QUERY_STALE_TIME, }, }, }) queryClient.mount() const stateElement document.getElementById(__QUERY_STATE__) if (!stateElement) { throw new Error(Missing dehydrated state script.) } hydrate(queryClient, JSON.parse(stateElement.textContent?.trim() ?? null)) appElement.queryClient queryClient window.addEventListener( pagehide, () { queryClient.unmount() }, { once: true }, ) await import(./app.js)指南对应的说明是在浏览器里解析状态、创建全新QueryClient、调用hydrate(queryClient, dehydratedState)、把 client 赋给服务端渲染出的元素之后才导入 Lit 组件。如果 client 是手动mount()的就在pagehide时unmount()。组件侧采用显式 client 模式指南 ssr.md 的 Component Pattern只有queryClient属性可用时才创建 controller而不是从 DOM 里的 provider 发现 client——因为 SSR 时 client 由渲染器创建不存在 connected 的 DOM providerprotected override willUpdate(): void { if (!this.dataQuery this.queryClient) { this.dataQuery createQueryController( this, createDataQueryOptions(this.apiBaseUrl), this.queryClient, ) } }完整组件见 examples/lit/ssr/src/app.ts查询定义queryKey、queryFn、staleTime、retry: false见 src/api.ts。运行示例并验证结果从仓库根目录运行pnpm --dir examples/lit/ssr run devdev脚本会先执行tsc --noEmit vite build构建客户端资源再用 tsx 启动 server/index.mjs。服务端默认监听127.0.0.1:4174启动日志形如端口与 public origin 取决于配置[ssr] listening on http://127.0.0.1:4174 (public origin http://127.0.0.1:4174)端口和 origin 可通过环境变量调整定义见 config/ports.jsSSR_PORT默认 4174、SSR_HOST默认 127.0.0.1、SSR_PUBLIC_ORIGIN显式指定对外 origin适合反代部署、SSR_PUBLIC_HOST。SSR_PUBLIC_ORIGIN会直接作为服务端fetch的绝对 API origin 使用。验证 SSR 与水合是否生效浏览器打开http://127.0.0.1:4174/。页面应直接显示组件渲染的内容标题 Lit Query SSR、状态 Ready、消息 Hello from SSR!示例的DEFAULT_MESSAGE、Request count 和 Served at。首屏就是 Ready 而非 Loading...说明服务端 HTML 已经带上了预取到的数据。响应头中的x-ssr-query-controller-created值大于 0说明服务端渲染确实经过了createQueryController示例服务端会在此检查失败时抛错。页面没有再发起新的数据请求水合恢复了缓存初始staleTime为 30 秒retry: false。点击页面上的 Refetch 按钮会触发一次/api/data请求页面 Request count 随之变化也可以直接请求GET /api/request-count查看服务端计数POST /api/reset会把计数等状态复位示例服务端内置的调试接口。出错时的排查点示例服务端把每类失败都变成了可观察的信号对照 server/index.mjs 与 src/main.ts构建产物缺失服务端读取不到dist/index.html时会抛Missing built client assets. Run pnpm --dir examples/lit/ssr run build from the repo root first.——先执行构建再启动服务端。预取失败或渲染抛错GET /返回 500 和SSR render failed.页面控制台打印[ssr] render failed:及具体错误其中预取状态不是success时的错误信息为SSR prefetch did not complete successfully. Refusing to render loading HTML.。客户端缺 dehydrated 状态脚本抛出Missing dehydrated state script.说明 HTML 模板里id__QUERY_STATE__的 script 没有正确嵌入。客户端找不到 SSR 元素抛出Expected the SSR app element to exist before hydration.说明__SSR_APP_HTML__占位符没有替换成真实的ssr-app节点。限制指南明确 Lit Query 适配器是实验性的API 在早期阶段生产使用建议锁定精确版本。安装文档说明 Lit Devtools 目前不可用这是当前适配器的限制不是缺失的安装步骤。客户端必须导入lit-labs/ssr-client/lit-element-hydrate-support.js并提供水合入口脚本服务端不能跨请求共享QueryClient。完成后如果需要调整行为可参照指南中的组件模式与 Lit Query 参考文档 继续深入 controller 和 provider 的完整契约。【免费下载链接】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),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价