ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

TanStack Start Static Server Functions:构建期预执行 + 静态缓存的全栈数据加速方案

TanStack Start Static Server Functions:构建期预执行 + 静态缓存的全栈数据加速方案 TanStack Start Static Server Functions构建期预执行 静态缓存的全栈数据加速方案【免费下载链接】router A client-first, server-capable, fully type-safe router and full-stack framework for the web (React and more).项目地址: https://gitcode.com/GitHub_Trending/ro/routerStatic Server Functions静态服务端函数是 TanStack Start 中一类特殊的服务端函数它们在构建期预渲染阶段执行一次将结果作为静态 JSON 资产缓存进构建产物客户端挂载后所有后续调用直接改走 fetch 静态文件从而把服务器计算彻底转变为静态资源分发。本文以官方指南 static-server-functions.md 为主体结合仓库内 start-static-server-functions 包源码、e2e 测试 与 start-basic-static 示例 展开讲解读完你将掌握该中间件的接入方式、运行机制、缓存键规则、与静态预渲染SSG的配合方式及其实验性限制。背景为什么需要把 Server Function 变成静态TanStack Start 的 Server Functions 允许你定义只在服务端运行的逻辑并从 loaders、组件、hooks 或其他服务端函数中类型安全地调用它。默认情况下每次客户端调用都会通过 RPC 走到真实服务器执行 handler。但在使用 静态预渲染Static Prerendering 部署纯静态站点时服务器可能根本不存在——或者你希望那些内容固定、不含用户身份的数据完全绕开服务器直接由 CDN/静态托管平台分发。Static Server Functions 正是为这种场景设计的在构建期把 handler 执行掉把结果以静态 JSON 形式缓存进产物运行时零服务器计算。适用前提非常明确该 Server Function 必须是确定性、无副作用、可公开缓存的——例如读取一份公开的文章列表、拉取外部公开 API 的数据并固化到产物中。任何依赖会话、Cookie、请求头或用户身份的 handler都不应使用本方案public缓存导致的跨租户数据泄漏风险请参考 Server Functions 指南中的 Cache-Control 安全说明。快速接入给 createServerFn 挂上 staticFunctionMiddleware文档给出的最小用法如下import { createServerFn } from tanstack/react-start import { staticFunctionMiddleware } from tanstack/start-static-server-functions const myServerFn createServerFn({ method: GET }) .middleware([staticFunctionMiddleware]) .handler(async () { return Hello, world! })关键约束来自原文档的警告staticFunctionMiddleware必须是最后一个finalmiddleware。原因是该中间件需要接管执行结果并写入缓存与从缓存读取结果这两端若其后还有其它中间件则缓存内容与真实最终响应之间会出现不一致。从仓库源码看该包导出即这一中间件本体index.ts其 peer 依赖同时声明了tanstack/react-start与tanstack/solid-start均为可选因此该模式在 React 与 Solid 的 Start 应用中都可使用包依赖tanstack/start-client-core提供createMiddleware与默认 seroval 插件和seroval负责跨端序列化引擎要求 Node.js 22.12.0见 package.json。组合其它中间件顺序很重要官方示例start-basic-static中展示了与自定义日志中间件共存的方式posts.tsximport { logMiddleware } from ./loggingMiddleware import { staticFunctionMiddleware } from tanstack/start-static-server-functions export const fetchPosts createServerFn({ method: GET }) .middleware([logMiddleware, staticFunctionMiddleware]) .handler(async () { return axios .getArrayPostType(https://jsonplaceholder.typicode.com/posts) .then((r) r.data.slice(0, 10)) })logMiddleware在前负责日志/统计等通用横切逻辑staticFunctionMiddleware殿后负责缓存读写——与final middleware约束一致。同样注意示例中的 handler 使用 GET 方法且数据来自公开 APIjsonplaceholder符合可静态化的前提。工作原理构建期执行 → 静态缓存 → 客户端水合 → fetch 替换原文档给出了一条清晰的时序整理如下Build-time构建期/预渲染期静态预渲染过程中带staticFunctionMiddleware的 Server Function 被执行一次执行结果被写入构建产物成为静态 JSON 文件键由函数 ID 参数/载荷哈希推导得出该结果同时作为正常返回值参与预渲染用于把页面 HTML 静态生成出来。Runtime运行时初始访问时浏览器直接拿到预渲染好的 HTML其中已内嵌该 Server Function 的数据客户端挂载后内嵌数据完成水合hydration此后任何客户端的再次调用都会被替换为对那个静态 JSON 文件的fetch请求——不再触碰真实服务器。也就是说这套方案天然与prerender配置绑定只有开启静态预渲染的应用才有构建期执行这一环节Static Server Functions 的缓存文件也是随预渲染一起产出的。预渲染的开启与配置方式见 静态预渲染指南在tanstackStart({ prerender: { enabled: true, ... } })中配置autoStaticPathsDiscovery、crawlLinks、filter等。源码级拆解client 端与 server 端各做什么中间件的完整实现位于 packages/start-static-server-functions/src/staticFunctionMiddleware.ts核心是createMiddleware({ type: function })上的.client()与.server()两个分支服务端分支构建期写入缓存.server(async (ctx) { const response await ctx.next() if ( process.env.NODE_ENV production process.env.TSS_CLIENT_OUTPUT_DIR ) { await addItemToCache({ functionId: ctx.serverFnMeta.id, response: { result: (response as any).result, context: ctx }, data: ctx.data, }) } return response })先正常执行后续逻辑ctx.next()拿到结果仅当NODE_ENV production且存在TSS_CLIENT_OUTPUT_DIR即客户端构建输出目录已确定时才把结果写入缓存缓存内容同时包含resulthandler 返回值与context.sendContext发送上下文因此响应头等上下文信息也能随静态文件一并恢复。客户端分支运行时读取缓存.client(async (ctx) { if ( process.env.NODE_ENV production // do not run this during SSR on the server typeof document ! undefined ) { const response await fetchItem({ functionId: ctx.serverFnMeta.id, data: ctx.data, }) if (response) { return { result: response.result, context: { ...(ctx as any).context, ...response.context }, } as any } } return ctx.next() })只在生产环境、且运行在浏览器环境typeof document ! undefined避免 SSR 阶段误走缓存时读取命中缓存则直接返回缓存结果未命中则回退到ctx.next()走常规 RPC该分支同时维护了一个内存级staticClientCacheMapstring, any减少重复请求见源码 staticFunctionMiddleware.ts 中的staticClientCache。缓存键与产物路径函数 ID 参数哈希这是理解整个方案的关键细节。缓存文件名由两段信息推导函数 IDfunctionId即 Server Function 在构建时生成的稳定函数 ID。关于 ID 的生成与自定义方式默认 SHA-256、冲突去重后缀、serverFns.generateFunctionId自定义等请参考 Server Functions 指南 中的 Function ID generation for production build 一节。参数哈希hash对传入的data即参数/载荷做一次文件名安全化处理得到的字符串。最终 URL 由源码中的getStaticCacheUrl生成const getStaticCacheUrl async (opts: { functionId: string; hash: string }) { const filename await sha1Hash(${opts.functionId}__${opts.hash}) return /__tsr/staticServerFnCache/${filename}.json }即把函数ID__参数哈希拼接后做 SHA-1 哈希得到最终文件名产物落在客户端输出目录下的/__tsr/staticServerFnCache/hash.json实际写入时通过path.join(process.env.TSS_CLIENT_OUTPUT_DIR, url)落盘。实现细节值得注意源码注释明确说明参数哈希并非直接序列化而是先把 JSON 的对象键排序sortedKeysReplacer再替换掉文件名非法字符/ \ ? % * : | →-空白 →_以保证同一份参数无论键顺序如何都映射到同一个缓存文件最大化缓存命中率这里使用的 SHA-1仅用于缩短缓存文件名非加密用途不应用于任何安全场景源码 JSDoc 中有明确警示。数据落盘前会经过toJSONAsync(..., { plugins: getDefaultSerovalPlugins() })序列化读取时用fromJSON反序列化因此非 JSON 原生类型Date、Map、Set 等 seroval 支持的类型同样能被正确缓存与还原——这一层正是依赖tanstack/start-client-core提供的默认 seroval 插件源码见 staticFunctionMiddleware.ts 的addItemToCache/fetchItem。完整落地示例loader 中的静态 Server Functione2e 测试目录 e2e/react-start/static-server-functions 提供了可直接对照的最小完整用例路由posts.tsx展示了静态化 Server Function loader 消费的标准写法import { Outlet, createFileRoute } from tanstack/react-router import { createServerFn } from tanstack/react-start import { staticFunctionMiddleware } from tanstack/start-static-server-functions type Post { id: string title: string } const fetchPosts createServerFn({ method: GET }) .middleware([staticFunctionMiddleware]) .handler(async () { return [ { id: 1, title: First Post }, { id: 2, title: Second Post }, { id: 3, title: Third Post }, ] as ArrayPost }) export const Route createFileRoute(/posts)({ loader: async () fetchPosts(), component: PostsComponent, }) function PostsComponent() { const posts Route.useLoaderData() return ( div h2 contenteditable="false">【免费下载链接】router A client-first, server-capable, fully type-safe router and full-stack framework for the web (React and more).项目地址: https://gitcode.com/GitHub_Trending/ro/router创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表