实战案例

案例 1:待办 CRUD

列表页 SSR 加载待办,表单通过 Server Function 创建数据,再刷新 loader。

const listTodos = createServerFn({ method: 'GET' }).handler(() => db.todo.findMany())

const createTodo = createServerFn({ method: 'POST' })
  .validator((input: { title: string }) => input)
  .handler(({ data }) => db.todo.create({ data }))

export const Route = createFileRoute('/todos')({
  loader: () => listTodos(),
  component: TodosPage,
})

function TodosPage() {
  const todos = Route.useLoaderData()
  const router = useRouter()

  async function onSubmit(title: string) {
    await createTodo({ data: { title } })
    await router.invalidate()
  }

  return <TodoList todos={todos} onSubmit={onSubmit} />
}

关键点:写操作必须验证输入;mutation 完成后失效 loader。

案例 2:受保护 Dashboard

export const Route = createFileRoute('/dashboard')({
  beforeLoad: ({ context }) => {
    if (!context.user) throw redirect({ to: '/login' })
  },
  loader: ({ context }) => getDashboard({ data: { userId: context.user.id } }),
  component: Dashboard,
})

beforeLoad 阻止未登录访问;Server Function 内仍需确认用户权限。

案例 3:Webhook API

export const Route = createFileRoute('/api/webhooks/stripe')({
  server: {
    handlers: {
      POST: async ({ request }) => {
        const raw = await request.text()
        await verifyStripeSignature(request.headers, raw)
        await handleStripeEvent(raw)
        return Response.json({ received: true })
      },
    },
  },
})

Webhook 是公开 HTTP endpoint,应使用 Server Route 而不是 Server Function。

常见反模式

反模式改进
mutation 用 GET使用 POST Server Function
只在组件中判断登录beforeLoad + 服务端授权
search params 手写解析使用 validateSearch
所有数据都放 loader路由关键数据用 loader,局部状态用 Query

下一步