跳转到内容

SPA + SSR 双模式:Vite 嵌入 Next.js 的架构

学习目标

理解 LobeHub 如何用 Vite SPA + Next.js SSR 构建双模式前端,包括多入口构建、Debug Proxy 开发模式和路线注册同步机制。

前置知识

本章无通用知识前置,直接聚焦 LobeHub 的工程实践。

项目实践

双模式架构

多入口构建

Vite 配置了 4 个独立入口,每个入口生成独立的 SPA 应用:

// 伪代码:Vite 多入口配置
export default defineConfig({
build: {
rollupOptions: {
input: {
web: "src/spa/entry.web.tsx",
mobile: "src/spa/entry.mobile.tsx",
desktop: "src/spa/entry.desktop.tsx",
popup: "src/spa/entry.popup.tsx",
},
},
},
});

每个入口使用 react-router-dom 配置独立的路线树。

Roots vs Features 路由约定

LobeHub 使用 roots vs features 的分离模式:

  • src/routes/(roots):只放路线段文件(_layout/index.tsxindex.tsx[id]/index.tsx),必须,只从 @/features/* 导入和组合布局
  • src/features/:业务组件按领域组织(如 PagesPageEditorHome),包含布局块、hooks 和领域特定 UI

禁止行为:不要在 src/routes/ 内新建 features/ 文件夹。

关键陷阱:路线注册同步

Desktop 路线树必须在两个配置文件中注册:

src/spa/router/desktopRouter.config.tsx
src/spa/router/desktopRouter.config.desktop.tsx

如果只更新其中一个:会导致白屏,因为另一个构建路径找不到该路线。

LobeHub 用自动化测试保护这个不变量:

desktopRouter.sync.test.tsx

该测试验证两个配置文件的路线树完全同步。

Debug Proxy 开发模式

运行 bun run dev:spa 后,终端会打印 Debug Proxy URL:

Debug Proxy: https://app.lobehub.com/_dangerous_local_dev_proxy?debug-host=http%3A%2F%2Flocalhost%3A9876

这个 URL 加载生产后端(app.lobehub.com)的 SPA 页面,但将 Vite dev server 作为 HMR 源。开发者可以在本地修改代码并看到实时更新,同时使用真实的服务器配置。

构建流水线

Terminal window
bun run build # 完整构建
bun run build:spa # Vite 构建 SPA
bun run build:spa:copy # 复制 SPA 产物到 Next.js 静态目录
bun run build:next # Next.js SSR 构建

SPA 模板生成build:spa:copy 不仅复制 SPA 产物,还生成 HTML 模板供 Next.js SSR 页面嵌入。

问题与规避

问题场景规避策略
白屏只更新了一个 desktopRouter config运行 desktopRouter.sync.test.tsx 检测
SPA 模板过期复制步骤未执行构建脚本强制执行 build:spa:copy
Mobile HMR 不工作使用了错误的 dev 命令使用 dev:spa:mobile 而非 dev:spa
Next.js 缓存导致过时 UI开发中切换分支rm -rf .next 清理缓存

设计取舍

为什么用 SPA + SSR 双模式而非纯 SSR?

纯 SSR 的每次页面切换都需要服务器往返,延迟高。SPA 通过客户端路由实现即时切换,支持 PWA 离线使用。

代价:两套构建系统维护成本高,SPA 模板需要复制到 Next.js 静态目录。

为什么用 Roots vs Features 分离?

传统的 Next.js 项目中,业务逻辑和页面组件混在 app/pages/ 目录下,随时间推移变得难以维护。Roots vs Features 分离确保:

  • 路线文件只负责布局组合
  • 业务逻辑在 features 中按领域组织
  • 新成员能快速定位代码

代价:增加了一层抽象,新成员需要理解约定。

参考来源

  • LobeHub Vite 配置 — vite.config.ts
  • LobeHub SPA 入口 — src/spa/entry.*.tsx
  • LobeHub SPA 路由 — src/spa/router/
  • LobeHub 路线同步测试 — src/spa/router/desktopRouter.sync.test.tsx