mcpField
D
deployment

deployment

github.com/tanstack云与 DevOps15.3k197 次安装6 天前更新
代码仓库

关于 deployment

面向使用 Vite 和 Nitro 构建的应用,介绍 TanStack Start 的部署与渲染配置。适合需要设置托管、SSR、预渲染和 SEO 元数据的开发者。

主要功能

  1. 01使用 Vite 和 Nitro 配置托管
  2. 02按路由配置 SSR
  3. 03静态预渲染
  4. 04元标签与结构化数据
  5. 05动态站点地图路由

适用场景

  1. 01配置应用在托管平台上的部署
  2. 02按路由选择服务端或客户端渲染
  3. 03在构建时生成静态 HTML
  4. 04设置路由专属 SEO 元数据

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/tanstack/router --skill deployment

安装后在 Claude Code 中输入/deployment确认已加载。

详细介绍

部署与渲染

TanStack Start 通过 Vite 和 Nitro 部署到任何托管服务提供商。本技能介绍托管设置、SSR 配置、预渲染和 SEO。

托管服务提供商

Cloudflare Workers
pnpm add -D @cloudflare/vite-plugin wrangler
// vite.config.ts
import { defineConfig } from 'vite'
import { tanstackStart } from '@tanstack/react-start/plugin/vite'
import { cloudflare } from '@cloudflare/vite-plugin'
import viteReact from '@vitejs/plugin-react'

export default defineConfig({
  plugins: [
    cloudflare({ viteEnvironment: { name: 'ssr' } }),
    tanstackStart(),
    viteReact(),
  ],
})
// wrangler.jsonc
{
  "name": "my-app",
  "compatibility_date": "2025-09-02",
  "compatibility_flags": ["nodejs_compat"],
  "main": "@tanstack/react-start/server-entry",
}

部署:npx wrangler login && pnpm run deploy

Worker 环境变量按请求注入。 Cloudflare Workers 会在请求时注入环境变量。即使在服务器上,模块作用域中的 process.env.X 也会求值为 undefined。在模块作用域中读取环境变量时,Cloudflare 推荐使用 cloudflare:workers 环境绑定:

import { env } from 'cloudflare:workers'
const apiHost = env.API_HOST

或者在 .handler() / 中间件 .server() 中按请求读取 process.env.X。请参阅 Cloudflare 的环境变量文档 和 start-core/execution-model。

Netlify
pnpm add -D @netlify/vite-plugin-tanstack-start
// vite.config.ts
import { defineConfig } from 'vite'
import { tanstackStart } from '@tanstack/react-start/plugin/vite'
import netlify from '@netlify/vite-plugin-tanstack-start'
import viteReact from '@vitejs/plugin-react'

export default defineConfig({
  plugins: [tanstackStart(), netlify(), viteReact()],
})

部署:npx netlify deploy

Nitro(Vercel、Railway、Node.js、Docker)
npm install nitro@npm:nitro-nightly@latest
// vite.config.ts
import { defineConfig } from 'vite'
import { tanstackStart } from '@tanstack/react-start/plugin/vite'
import { nitro } from 'nitro/vite'
import viteReact from '@vitejs/plugin-react'

export default defineConfig({
  plugins: [tanstackStart(), nitro(), viteReact()],
})

构建并启动:npm run build && node .output/server/index.mjs

Bun

Bun 部署需要 React 19。对于 React 18,请使用 Node.js 部署。

// vite.config.ts — add bun preset to nitro
plugins: [tanstackStart(), nitro({ preset: 'bun' }), viteReact()]

选择性 SSR

使用 ssr 属性控制每条路由的 SSR。

ssr: true(默认)

在服务器上运行 beforeLoad 和 loader,并在服务器上渲染组件:

export const Route = createFileRoute('/posts/$postId')({
  ssr: true, // default
  loader: () => fetchPost(), // runs on server during SSR
  component: PostPage, // rendered on server
})
ssr: false

禁用服务器对 beforeLoad/loader 的执行和服务器渲染:

export const Route = createFileRoute('/dashboard')({
  ssr: false,
  loader: () => fetchDashboard(), // runs on client only
  component: DashboardPage, // rendered on client only
})
ssr: 'data-only'

在服务器上运行 beforeLoad/loader,但仅在客户端渲染组件:

export const Route = createFileRoute('/canvas')({
  ssr: 'data-only',
  loader: () => fetchCanvasData(), // runs on server
  component: CanvasPage, // rendered on client only
})
函数形式

根据 params/search 在运行时决定是否使用 SSR:

export const Route = createFileRoute('/docs/$docType/$docId')({
  ssr: ({ params }) => {
    if (params.status === 'success' && params.value.docType === 'sheet') {
      return false
    }
  },
})
SSR 继承

子路由会继承父路由的 SSR 配置,且只能设置得更严格:

  • true → data-only 或 false(允许)
  • false → true(不允许——以父路由的 false 为准)
默认 SSR

在 src/start.ts 中更改所有路由的默认设置:

import { createStart } from '@tanstack/react-start'

export const startInstance = createStart(() => ({
  defaultSsr: false,
}))

静态预渲染

在构建时生成静态 HTML:

// vite.config.ts
tanstackStart({
  prerender: {
    enabled: true,
    crawlLinks: true,
    concurrency: 14,
    failOnError: true,
  },
})

静态路由会自动发现。动态路由(例如 /users/$userId)需要 crawlLinks 或显式配置 pages。

SEO 和 Head 管理

基本 Meta 标签
export const Route = createFileRoute('/')({
  head: () => ({
    meta: [
      { title: 'My App - Home' },
      { name: 'description', content: 'Welcome to My App' },
    ],
  }),
})
根据 Loader 数据动态生成 Meta
export const Route = createFileRoute('/posts/$postId')({
  loader: async ({ params }) => fetchPost(params.postId),
  head: ({ loaderData }) => ({
    meta: [
      { title: loaderData.title },
      { name: 'description', content: loaderData.excerpt },
      { property: 'og:title', content: loaderData.title },
      { property: 'og:image', content: loaderData.coverImage },
    ],
  }),
})
结构化数据(JSON-LD)
head: ({ loaderData }) => ({
  scripts: [
    {
      type: 'application/ld+json',
      children: JSON.stringify({
        '@context': 'https://schema.org',
        '@type': 'Article',
        headline: loaderData.title,
      }),
    },
  ],
})
通过服务器路由动态生成 Sitemap
// src/routes/sitemap[.]xml.ts
export const Route = createFileRoute('/sitemap.xml')({
  server: {
    handlers: {
      GET: async () => {
        const posts = await fetchAllPosts()
        const sitemap = `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  ${posts.map((p) => `<url><loc>https://myapp.com/posts/${p.id}</loc></url>`).join('')}
</urlset>`
        return new Response(sitemap, {
          headers: { 'Content-Type': 'application/xml' },
        })
      },
    },
  },
})

常见错误

1. 高:Cloudflare Workers 缺少 nodejs_compat 标志
// WRONG — Node.js APIs fail at runtime
{ "compatibility_flags": [] }

// CORRECT
{ "compatibility_flags": ["nodejs_compat"] }
2. 中:使用 React 18 部署 Bun

Bun 专用部署仅适用于 React 19。对于 React 18,请使用 Node.js 部署。

3. 中:子路由放宽父路由的 SSR 配置
// Parent sets ssr: false
// WRONG — child cannot upgrade to ssr: true
const parentRoute = createFileRoute('/dashboard')({ ssr: false })
const childRoute = createFileRoute('/dashboard/stats')({
  ssr: true, // IGNORED — parent false wins
})

// CORRECT — children can only be MORE restrictive
const parentRoute = createFileRoute('/dashboard')({ ssr: 'data-only' })
const childRoute = createFileRoute('/dashboard/stats')({
  ssr: false, // OK — more restrictive than parent
})

交叉参考

评论

支持 Markdown