00 / 00

设计理念与技术选型

理解 01MVP 模板为什么使用 TanStack Start、monorepo、共享包和 SSR,以及以后扩展到移动端时应该复用哪些部分

01MVP 模板的目标很明确:让一个人或小团队能快速做出可以上线、可以收费、可以继续维护的产品。

它不追求把所有技术都放进来。这里的选择围绕四件事展开:

  • 启动快:账号、数据库、支付、AI、文档、部署都有默认路径。
  • 边界清楚:页面、业务能力、接口、数据库、共享组件各有位置。
  • 后续能长大:从单个 Web 产品开始,保留 API、数据库和共享包的扩展空间。
  • AI 能协作:仓库规则、文档结构和 package 边界足够明确,AI 编码工具不需要猜。

为什么用 TanStack Start

这套模板选择 TanStack Start,是因为它同时满足几个早期产品常见需求:

  • React 生态完整,适合快速搭建产品界面。
  • Vite 构建速度快,本地开发体验好。
  • 文件路由清楚,页面和布局容易定位。
  • SSR 能让首页、文档、定价页、案例页更适合搜索引擎和分享链接。
  • 和 TanStack Query 配合自然,服务端预取和客户端缓存可以使用同一套数据模型。

TanStack Start 的一个重要特点是:很多代码默认会在服务端和客户端都运行。它的 loader 也不是传统意义上的服务端专用函数。需要访问数据库、密钥、支付接口、私有 API 时,应该放到服务端函数、Hono/oRPC 接口或后端 package 里。

为什么保留 SSR

如果产品只有登录后的后台,纯客户端应用也能完成很多需求。但 01MVP 面向的是“要发布、要介绍、要获客、要收费”的产品,通常会有这些页面:

  • 首页
  • 定价页
  • 文档
  • 案例
  • 支付说明
  • SEO 页面
  • 分享落地页

这些页面适合由服务端先渲染出稳定 HTML。用户打开更快,搜索引擎和社交平台也更容易读取页面内容。

SSR 带来的代价是前端组件要更守规矩:服务端渲染出的 HTML 必须和客户端接管时的第一帧一致。主题、浏览器语言、localStorage、屏幕宽度、时区、随机数这些浏览器状态,不能直接决定服务端输出的 JSX。

模板里的做法是:

  • 首屏关键数据用服务端可见的来源,例如 cookie、请求头、loader 预取或 React Query 脱水数据。
  • 浏览器偏好先渲染稳定默认值,hydration 后再增强。
  • 完全依赖浏览器 API 的组件放到 client-only 边界里。
  • 文档、首页、定价页这类公开页面尽量写成 SSR-safe 组件。

以后做 APP 怎么理解

Web SSR 页面不会直接变成原生 APP 或小程序页面。DOM、CSS、shadcn/ui、TanStack Router 的页面结构,都属于 Web 客户端。

真正应该复用的是这些底层能力:

  • 数据库 schema 和迁移
  • 认证模型和用户体系
  • oRPC/Hono 暴露出来的业务接口
  • 输入校验、权限判断、订单、积分、AI 调用等业务规则
  • packages/* 里的共享类型和服务逻辑

所以这套模板的长期边界是:

层级当前用途未来 APP 是否复用
products/01mvp/apps/webWeb 页面、SSR、文档、控制台主要复用接口调用方式和产品逻辑,UI 通常重写
products/01mvp/packages/api业务接口、oRPC 路由、服务编排高度复用
products/01mvp/packages/dbschema、迁移、数据库访问高度复用
products/01mvp/packages/auth登录、会话、用户能力高度复用
products/01mvp/packages/config01MVP 环境默认值和产品配置入口高度复用
packages/uiWeb UI 原语只在 Web 复用
packages/config共享环境变量 schema部分复用

如果未来要做移动端、小程序或桌面端,建议新建一个客户端应用,让它调用同一套 API 和业务 package。不要把 Web 页面组件当成跨端复用层。

为什么用 monorepo 和 products 结构

一个早期产品通常会同时有 Web 应用、API、认证、数据库、UI、日志、AI、支付、存储等模块。一个长期做 Web coding 的仓库还会继续长出多个产品、多个客户端、内部工具、Agent Skills 和一套自己的工程规范。把它们放进一个仓库,能减少很多同步成本:

  • 改 schema 时,API 和页面能在同一次提交里同步调整。
  • 共享类型不需要发布 npm 包才能使用。
  • 统一 lint、type-check、build 和测试命令。
  • AI 编码工具能一次看到完整上下文。
  • 后续拆服务时,有明确 package 边界可以参考。

products/<product> 这一层用来隔离具体产品。01MVP、OneSay 或未来的新项目可以共享根目录的工具链、规范和通用包,但各自保留自己的 apps、API、DB、auth、config 和业务逻辑。这样不用在多个仓库之间复制基础设施,也不会让一个产品的代码默认渗透到另一个产品。

monorepo 的代价是目录会比单应用项目多一些。所以模板要求 route 文件保持薄,业务能力沉到 products/<product>/packages、根共享 packages 或 slice-local 模块里。

结构设计原则

route 只负责入口

products/01mvp/apps/web/src/routes 负责 URL、布局、鉴权、head()、loader 和页面挂载。不要把复杂业务逻辑写进 route 文件。

页面 UI 放到 products/01mvp/apps/web/src/pages,可复用业务组件放到 products/01mvp/apps/web/src/features,布局和组合型区域放到 products/01mvp/apps/web/src/widgets

API 是跨端边界

Web 可以直接使用 TanStack Start 和 oRPC,但业务能力不应该被锁在页面组件里。新增产品能力时,优先思考它是不是应该有清晰的 API contract。

未来如果做 APP、小程序、开放接口或后台任务,这个边界会决定迁移成本。

packages 存放可复用能力

packages 不是杂物间。只有多个产品会复用、或者需要和 Web 客户端解耦的能力,才适合放进根目录 packages/*。只属于某个产品、但会被该产品多个 app/package 复用的能力,放进 products/<product>/packages/*

典型例子:

  • products/01mvp/packages/api:业务接口和服务编排
  • products/01mvp/packages/config:01MVP 产品默认值和 env 入口
  • products/01mvp/packages/db:数据库 schema 和迁移
  • products/01mvp/packages/auth:认证和用户能力
  • products/01mvp/packages/i18n:消息和语言运行时
  • packages/ui:跨产品 Web UI 原语
  • packages/email:跨产品邮件发送与模板基础设施
  • packages/logger:结构化日志

单个页面自己的状态、弹窗、表单逻辑,优先留在对应页面或 feature 目录。

技术栈选择

模块选择原因
Web 框架TanStack StartReact + Vite + SSR,适合产品站、文档和登录后应用放在同一套工程里
路由TanStack Router类型安全路由,文件结构清楚,适合复杂布局和受保护页面
数据请求TanStack Query + oRPC客户端缓存、服务端预取、类型安全接口可以连在一起
HTTP 服务Hono轻量,适合 API、Webhook、健康检查和特殊 HTTP 路由
数据库PostgreSQL + Drizzleschema、迁移和类型在 TypeScript 项目里更容易维护
认证Better Auth覆盖邮箱、手机号、OAuth、Magic Link 等常见登录方式
UITailwind CSS v4 + shadcn/ui组件可控,主题可改,适合做产品后台和文档站
国际化Paraglide翻译 key 明确,适合和路由、构建、AI 翻译工作流配合
文档FumadocsMDX 文档、侧边栏、搜索和多语言文档比较完整
工作区Vite Plus + pnpm workspace统一 monorepo 命令、依赖和检查流程
部署Zeabur + Cloudflare,Cloudflare Workers 可选默认部署路径低门槛,同时保留边缘部署空间

做功能时的判断顺序

新增功能前,可以按这个顺序判断放在哪里:

  1. 它是否只是一个页面展示?放到 products/01mvp/apps/web/src/pages
  2. 它是否是页面之间复用的 Web 功能?放到 products/01mvp/apps/web/src/featureswidgets
  3. 它是否只属于 01MVP 但会被 01MVP 的 API、后台任务、未来 APP 复用?放到 products/01mvp/packages/*
  4. 它是否会被多个产品复用?放到根目录 packages/*,保持产品无关。
  5. 它是否访问数据库、密钥、支付、私有服务?放到服务端边界里。
  6. 它是否依赖浏览器状态?让服务端和 hydration 第一帧先渲染稳定结果,再在客户端增强。

这个判断顺序能降低两个长期风险:Web 页面越来越难迁移,SSR 页面越来越容易出现 hydration 问题。

推荐阅读

这篇文档有问题?