--- sidebar_position: 4 title: 前端架构 sidebar_label: 前端架构 description: Plaza Next.js 前端架构、页面规范、组件设计、渲染策略和交互规则 --- # 前端架构 ## 项目初始化 ```bash cd ui/ pnpm create next-app@latest plaza --typescript --tailwind --app --no-src-dir cd plaza pnpm add swr ``` `.env.local`: ```bash NEXT_PUBLIC_API_BASE=https://go.tkmind.cn NEXT_PUBLIC_PLAZA_BASE=/plaza ``` --- ## 目录结构 ```text ui/plaza/ ├── app/ │ ├── layout.tsx # 根布局:全局 Header、Footer、主题 │ ├── plaza/ │ │ ├── page.tsx # /plaza 广场首页(SSR) │ │ ├── cat/ │ │ │ └── [slug]/ │ │ │ └── page.tsx # /plaza/cat/:slug 分类页(SSR) │ │ └── p/ │ │ └── [id]/ │ │ └── page.tsx # /plaza/p/:id 帖子详情(SSR) │ └── u/ │ └── [slug]/ │ └── page.tsx # /u/:slug 用户主页(SSR) ├── components/ │ ├── layout/ │ │ ├── Header.tsx # 顶部导航:Logo、分类、登录入口 │ │ ├── Footer.tsx # 底部:关于、版权、「用 MindSpace 制作」 │ │ └── MobileNav.tsx # 移动端底部导航 │ ├── feed/ │ │ ├── PostGrid.tsx # 响应式瀑布流容器 │ │ ├── PostCard.tsx # 单张卡片 │ │ ├── CategoryNav.tsx # 分类导航 Tab │ │ ├── FeedTabs.tsx # 「热门」/「最新」切换 │ │ └── InfiniteScroll.tsx # 无限滚动加载 │ ├── post/ │ │ ├── PostEmbed.tsx # 帖子详情页嵌入发布 bundle 的 iframe │ │ ├── PostMeta.tsx # 标题、作者、发布时间 │ │ ├── PostActions.tsx # 点赞、收藏、分享按钮 │ │ └── CommentSection.tsx # 评论区 │ ├── comment/ │ │ ├── CommentList.tsx │ │ ├── CommentItem.tsx │ │ └── CommentInput.tsx │ └── user/ │ ├── UserCard.tsx # 用户主页头部:头像、粉丝数、关注按钮 │ └── UserPostGrid.tsx # 用户发布的帖子网格 ├── lib/ │ ├── api.ts # 封装 fetch,调用 /api/plaza/v1/* │ ├── cache.ts # Next.js fetch 缓存策略常量 │ └── format.ts # 数字格式化(1.2万、3.4k) └── types/ └── plaza.ts # PlazaPost、PlazaComment 等类型定义 ``` --- ## 页面规范 ### /plaza 广场首页 **渲染方式**:SSR + ISR(每 60 秒重新生成) **布局**: ```text ┌─────────────────────────────────────────┐ │ Header:Logo 分类导航 搜索 登录 │ ├─────────────────────────────────────────┤ │ FeedTabs:[ 热门 ] [ 最新 ] │ │ FeaturedBanner(Sprint 5,精选轮播) │ ├─────────────────────────────────────────┤ │ │ │ PostGrid(瀑布流,2列移动/3列平板/4列桌面)│ │ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ │ │ │ 卡片 │ │ 卡片 │ │ 卡片 │ │ 卡片 │ │ │ └──────┘ └──────┘ └──────┘ └──────┘ │ │ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ │ │ │ 卡片 │ │ 卡片 │ │ 卡片 │ │ 卡片 │ │ │ └──────┘ └──────┘ └──────┘ └──────┘ │ │ [ 加载更多 / 无限滚动 ] │ ├─────────────────────────────────────────┤ │ Footer │ └─────────────────────────────────────────┘ ``` **首屏数据**:服务端获取第一页 20 条热门帖子,直接注入 HTML。后续翻页由客户端 SWR 请求。 --- ### /plaza/cat/:slug 分类页 **渲染方式**:SSR + `generateStaticParams`(所有分类在构建时预生成) 与首页布局相同,增加分类标题和描述,CategoryNav 高亮当前分类。 --- ### /plaza/p/:id 帖子详情 **渲染方式**:SSR,`cache: 'no-store'`(实时获取最新互动数据) **布局**: ```text ┌─────────────────────────────────────────┐ │ Header │ ├─────────────────────────────────────────┤ │ 作者信息:头像 名字 发布时间 关注按钮 │ ├─────────────────────────────────────────┤ │ │ │ PostEmbed │ │ ┌───────────────────────────────────┐ │ │ │ iframe src={bundle_url} │ │ │ │ sandbox="allow-scripts │ │ │ │ allow-same-origin" │ │ │ │ height 自适应内容高度 │ │ │ └───────────────────────────────────┘ │ │ │ ├─────────────────────────────────────────┤ │ PostActions:👍 1.2k 🔖 234 📤 分享 │ ├─────────────────────────────────────────┤ │ CommentSection(客户端渲染,登录后可写)│ ├─────────────────────────────────────────┤ │ Footer:「用 MindSpace 制作 →」 │ └─────────────────────────────────────────┘ ``` **iframe 规则**: - `src` 直接指向 `publications.bundle_url`,不通过广场服务转发。 - `sandbox` 属性限制:`allow-scripts allow-same-origin`,禁止 `allow-forms allow-popups allow-top-navigation`。 - `width: 100%`,高度通过 `postMessage` 从 bundle 内部上报,iframe 动态调整。 - bundle 域名与广场域名不同(`assets.go.tkmind.cn`)时,CSP 已在 MindSpace 发布流程中配置,无需广场额外处理。 --- ### /u/:slug 用户主页 **渲染方式**:SSR,`revalidate: 300`(5 分钟缓存) **布局**: ```text ┌─────────────────────────────────────────┐ │ Header │ ├─────────────────────────────────────────┤ │ 用户信息区 │ │ 头像(大) 显示名 @slug │ │ 简介文字 │ │ 📝 N 篇作品 👥 N 粉丝 ❤️ N 点赞 │ │ [ 关注 ] [ 发消息(占位)] │ ├─────────────────────────────────────────┤ │ 作品网格(同 PostGrid) │ └─────────────────────────────────────────┘ ``` --- ## PostCard 卡片组件规范 ```text ┌──────────────────────────┐ │ 封面图(16:9 或 4:3) │ │ cover_url │ ├──────────────────────────┤ │ 标题(最多 2 行截断) │ │ 摘要(最多 2 行截断) │ ├──────────────────────────┤ │ 头像 显示名 发布时间 │ ├──────────────────────────┤ │ 👍 1.2k 💬 34 │ └──────────────────────────┘ ``` **必须处理的状态**: - 封面图加载失败 → 显示分类颜色背景 + 标题首字 - 封面图未生成(`cover_url` 为空)→ 同上 - 标题超长 → CSS `line-clamp-2` - 数字格式:`1000` → `1k`,`10000` → `1万`,`100000` → `10万` --- ## 全局交互规则 ### 登录态感知 广场页面游客可以完整浏览,但以下操作需要登录: - 点赞、收藏、分享(分享本身可以不登录,但计数写入需要) - 发表评论 - 关注创作者 触发时弹出引导注册/登录浮层,不跳转页面(不打断浏览体验)。 ### 水印入口 每个帖子详情页底部 Footer 固定展示: ``` 用 MindSpace 制作 → [立即免费体验] ``` 点击跳转到 MindSpace 注册页,URL 附带 UTM 参数: ``` https://go.tkmind.cn/?utm_source=plaza&utm_medium=footer&utm_campaign=post_watermark&ref={post_id} ``` ### 分享 分享按钮提供三种方式: 1. 复制链接(`/plaza/p/{id}`) 2. 生成分享图片(封面 + 标题 + 作者名,服务端生成 OG 图) 3. 微信分享(JS-SDK,需要公众号配置) --- ## SEO meta 规范 完整规范(Open Graph、JSON-LD、canonical、robots)以 [07-seo](./07-seo) 为**唯一权威来源**。前端实现时: - 每个 SSR 页面在 `generateMetadata` 中调用共享 helper(`lib/metadata.ts`),避免在组件内重复字段。 - [03-frontend](./03-frontend) 只描述页面结构与布局;修改 meta 时只改 `07-seo` 与 helper。 --- ## 响应式断点 | 断点 | 卡片列数 | 说明 | | --- | --- | --- | | < 640px | 2 列 | 手机竖屏 | | 640px – 1024px | 3 列 | 手机横屏 / 平板 | | > 1024px | 4 列 | 桌面 | 移动端底部导航固定展示:首页、分类、发布(跳转 MindSpace)、我的。 --- ## 性能目标 | 指标 | 目标 | | --- | --- | | 首页 LCP | < 2.5s | | 首页 INP | < 200ms | | 首页 CLS | < 0.1 | | 帖子详情首字节时间 | < 500ms | | 图片格式 | WebP,next/image 自动优化 | | 首屏帖子数 | 20 条,图片懒加载 |