
从零构建一个 TanStack Start 应用手写配置、首个路由与 Server Function 实战【免费下载链接】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导读本文基于 TanStack Start 官方《Build a Project from Scratch》指南带你完全脱离脚手架用npm init手工搭建一个最基础的 TanStack Start 全栈应用既能渲染首页又能在服务器端读取计数、在客户端点击按钮递增计数最终在浏览器里看到服务器与客户端协同工作的真实效果。读完本文你将掌握TypeScript 与构建工具Vite / Rsbuild的完整配置、Router 与 Root 两个必需文件的职责、文件路由的写法以及createServerFn驱动的 Server Function 数据流。仓库中对应的可直接运行的完整参考实现位于 examples/react/start-counter。说明如果你已经通过 CLI 或克隆示例创建过项目可以跳过本指南直接进入 Routing 指南。准备工作初始化项目目录创建一个新的项目目录并完成 npm 初始化mkdir myApp cd myApp npm init -y本指南的所有命令均以npm演示你也可以使用自己偏好的包管理器pnpm / yarn / bun执行等价操作。仓库内示例项目 examples/react/start-counter/package.json 即使用 pnpm 管理依赖。TypeScript 配置TanStack Start 强烈推荐使用 TypeScript。在项目根目录创建tsconfig.json至少包含以下设置{ compilerOptions: { jsx: react-jsx, moduleResolution: Bundler, module: ESNext, target: ES2022, skipLibCheck: true, strictNullChecks: true } }要点说明jsx: react-jsx启用 React 17 的 JSX 转换无需显式import React示例源码 examples/react/start-counter/src/routes/index.tsx 即未导入 React 而直接使用 JSX。moduleResolution: Bundlermodule: ESNext适配 Vite / Rsbuild 这类打包器的解析规则。target: ES2022保证现代语法可用且兼容 Node 运行时。⚠️ 注意不要开启verbatimModuleSyntax否则可能导致服务器端 bundle 泄漏进客户端 bundle。示例项目的tsconfig.json见 examples/react/start-counter/tsconfig.json额外开启了strict、isolatedModules、noEmit等并配置了~/*路径别名映射到./src/*可作为更完整的上手模板。安装依赖TanStack Start 由 TanStack Router 驱动构建工具支持Vite或Rsbuild。首先安装 Start 与 Routernpm i tanstack/react-start tanstack/react-router然后安装 Reactnpm i react react-dom接着按你选择的构建工具安装对应依赖Vite / Rsbuild 二选一# Vite npm i -D vite vitejs/plugin-react # Rsbuild npm i -D rsbuild/core rsbuild/plugin-react如果你使用 Vite也可以用vitejs/plugin-react-swc替代vitejs/plugin-reactSWC 版本编译更快。最后安装 TypeScript 及类型声明npm i -D typescript types/react types/react-dom types/node从仓库的依赖声明packages/react-start/package.json可以确认tanstack/react-start的 peerDependencies 要求react 18.0.0 || 19.0.0并将vite、rsbuild/core、vitejs/plugin-rsc标记为可选peerDependenciesMeta这正是Vite 或 Rsbuild 任选其一的兼容性来源。更新配置文件package.json在package.json中设置type: module并添加构建工具 CLI 脚本// Vite { // ... type: module, scripts: { dev: vite dev, build: vite build } }// Rsbuild { // ... type: module, scripts: { dev: rsbuild dev, build: rsbuild build } }构建工具插件配置配置 TanStack Start 的构建工具插件注意 Vite 版要求React 插件必须放在 Start 插件之后// vite.config.ts (Vite) import { defineConfig } from vite import { tanstackStart } from tanstack/react-start/plugin/vite import viteReact from vitejs/plugin-react export default defineConfig({ server: { port: 3000, }, resolve: { tsconfigPaths: true, }, plugins: [ tanstackStart(), // reacts vite plugin must come after starts vite plugin viteReact(), ], })// rsbuild.config.ts (Rsbuild) import { defineConfig } from rsbuild/core import { pluginReact } from rsbuild/plugin-react import { tanstackStart } from tanstack/react-start/plugin/rsbuild export default defineConfig({ server: { port: 3000, }, plugins: [pluginReact(), tanstackStart()], })tanstackStart()插件是 Start 的核心入口它负责扫描src/routes目录生成路由树、注入 SSR / RSC 所需的构建管线、编译 Server Function 等。从包结构看该插件分别暴露于 tanstack/react-start/plugin/vite 与plugin/rsbuild两个子路径见 packages/react-start/package.json 的 exports 字段。必需的两个文件TanStack Start 应用有2 个必需文件Router 配置src/router.tsx——决定 TanStack Router 的行为应用根组件src/routes/__root.tsx——包裹所有其他路由的入口。配置完成后文件树大致如下. ├── src/ │ ├── routes/ │ │ └── __root.tsx │ ├── router.tsx │ ├── routeTree.gen.ts ├── vite.config.ts 或 rsbuild.config.ts ├── package.json └── tsconfig.json说明此时你还没有routeTree.gen.ts该文件会在首次运行 TanStack Start 时自动生成。它是文件路由的核心产物把src/routes下每个文件映射为类型安全的Route对象。参考实现见 examples/react/start-counter/src/routeTree.gen.ts文件头部注释明确提示此文件由 TanStack Router 自动生成不应手动修改且应将其排除在 linter / formatter 之外。Router 配置src/router.tsx用于配置 Router 的一切行为从默认的预加载preloading策略到数据加载缓存caching staleness等。最简配置如下// src/router.tsx import { createRouter } from tanstack/react-router import { routeTree } from ./routeTree.gen export function getRouter() { const router createRouter({ routeTree, scrollRestoration: true, }) return router }示例项目在此基础上还增加了类型注册让全应用获得完整的路由类型推导见 examples/react/start-counter/src/router.tsxdeclare module tanstack/react-router { interface Register { router: ReturnTypetypeof getRouter } }应用根组件src/routes/__root.tsx是所有路由的入口代码会包裹应用中所有其他路由// src/routes/__root.tsx import type { ReactNode } from react import { Outlet, createRootRoute, HeadContent, Scripts, } from tanstack/react-router export const Route createRootRoute({ head: () ({ meta: [ { charSet: utf-8, }, { name: viewport, content: widthdevice-width, initial-scale1, }, { title: TanStack Start Starter, }, ], }), component: RootComponent, }) function RootComponent() { return ( RootDocument Outlet / /RootDocument ) } function RootDocument({ children }: Readonly{ children: ReactNode }) { return ( html head HeadContent / /head body {children} Scripts / /body /html ) }两个关键组件各司其职HeadContent /把路由定义中head()声明的 meta 信息渲染进head实现按路由维度的 SEO / meta 管理Scripts /注入 TanStack Start 的客户端水合脚本是 SSR 产物在浏览器端复活为交互应用的关键Outlet /子路由组件的渲染插槽。示例项目还在RootComponent中加入了TanStackRouterDevtools /见 examples/react/start-counter/src/routes/__root.tsx开发时可直接看到路由状态面板生产构建会自动剔除。编写你的第一个路由服务器与客户端协同的计数器在src/routes目录新建文件即可创建路由。下面是一个读取文件 → 服务器端函数 → 客户端触发 → 刷新数据的完整计数器示例与 examples/react/start-counter/src/routes/index.tsx 一致// src/routes/index.tsx import * as fs from node:fs import { createFileRoute, useRouter } from tanstack/react-router import { createServerFn } from tanstack/react-start const filePath count.txt async function readCount() { return parseInt( await fs.promises.readFile(filePath, utf-8).catch(() 0), ) } const getCount createServerFn({ method: GET, }).handler(() { return readCount() }) const updateCount createServerFn({ method: POST }) .validator((d: number) d) .handler(async ({ data }) { const count await readCount() await fs.promises.writeFile(filePath, ${count data}) }) export const Route createFileRoute(/)({ component: Home, loader: async () await getCount(), }) function Home() { const router useRouter() const state Route.useLoaderData() return ( button typebutton onClick{() { updateCount({ data: 1 }).then(() { router.invalidate() }) }} Add 1 to {state}? /button ) }关键机制逐行拆解1. Server FunctioncreateServerFncreateServerFn是 TanStack Start 的核心 API它把一段看起来在客户端文件里的函数编译为可被客户端安全调用的 RPC。createServerFn与createStart、createMiddleware等一起由 tanstack/start-client-core 统一导出并重新暴露到tanstack/react-start根入口因此路由文件可以直接import { createServerFn } from tanstack/react-start。getCount声明method: GEThandler 在服务器端读取count.txt文件不存在时回退为0updateCount声明method: POST通过.validator((d: number) d)声明入参校验handler 接收{ data }并把计数写回文件从源码结构可以推断method用于生成对应的 HTTP 请求语义GET 幂等读取、POST 写入修改validator在请求进入服务器 handler 前完成参数校验。2. 路由定义与数据加载export const Route createFileRoute(/)({ component: Home, loader: async () await getCount(), })createFileRoute(/)声明该文件对应路径/的路由文件路由约定src/routes/index.tsx→/loader在导航完成前执行的数据加载钩子这里调用服务器函数getCount()取回初始计数天然支持 SSR 场景下的预取与序列化。3. 客户端交互与数据刷新const router useRouter() const state Route.useLoaderData() onClick{() { updateCount({ data: 1 }).then(() { router.invalidate() }) }}Route.useLoaderData()类型安全的 loader 数据访问钩子state的类型由getCount的返回值自动推导为number点击按钮 → 调用updateCount({ data: 1 })POST 到服务器写文件→ 成功后调用router.invalidate()让 loader 重新执行从而刷新页面上的计数。首次运行npm run dev启动开发服务器后访问http://localhost:3000。首次运行时 TanStack Start 会自动生成src/routeTree.gen.ts。你会在页面上看到一个按钮显示当前从服务器读取的计数每点击一次计数就会持久化到服务器端的count.txt文件并重新渲染——这就是客户端触发、服务器执行、数据回流的最小完整闭环。从零手写 vs. 官方示例对照本文所有代码都能在仓库中找到可直接运行的完整版本文件本文手写版仓库参考实现路由定义src/routes/index.tsxexamples/react/start-counter/src/routes/index.tsx根组件src/routes/__root.tsxexamples/react/start-counter/src/routes/__root.tsxRouter 配置src/router.tsxexamples/react/start-counter/src/router.tsx构建配置vite.config.tsexamples/react/start-counter/vite.config.ts自动生成文件src/routeTree.gen.tsexamples/react/start-counter/src/routeTree.gen.ts示例项目的package.jsonexamples/react/start-counter/package.json还演示了生产构建脚本build: vite build tsc --noEmit以及preview/start命令可作为你完善手写项目的参考。下一步至此你已经完成了一个 TanStack Start 项目的手写搭建并实现了首个带 Server Function 的路由。接下来可以学习文件路由的更多写法与嵌套布局Routing 指南深入了解数据预取、缓存与失效策略Data Loading 指南需要上线部署时参考 Hosting 部署指南。【免费下载链接】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),仅供参考