☰
成为全栈·Next.js 网站前台篇·总复盘:做一个公开内容站的真实成本
2026/10/8 13:01:42 网站建设 项目流程

成为全栈·Next.js 网站前台篇·总复盘:做一个公开内容站的真实成本

页面数量不是公开内容站成本的主要来源。真正昂贵的是公开与私有数据共存、缓存的新鲜度承诺、会话竞态、内容安全,以及每一种“完成”都需要相应证据。

前言

M3 系列从 App Router 写到 Cloudflare,一共 24 篇。回头看,首页、列表、详情、会员中心和投稿编辑器都不是最困难的部分;真正反复消耗时间的,是边界没有天然答案。

哪些内容在服务端,哪些交给浏览器?公开数据可以旧多久?会员状态怎样避免串账号?接口失败时页面该保留什么?本地 Worker 通过以后,我们究竟能声称完成了什么?

这篇不再介绍一个组件,而是复盘这套前台为什么比“做若干页面”昂贵,以及哪些经验可以带到下一个项目。

最初看见的是页面,后来面对的是生命周期

URL 请求 → 路由与 metadata → 服务端数据和公开缓存 → HTML / RSC 交付 → 客户端 hydration 与会话恢复 → 私有互动、错误恢复和重新验证

在 CSR 管理后台里,前端主要管理浏览器生命周期;App Router 内容站让前端同时参与服务端请求、缓存和 HTTP 响应。技术栈没有取消后端思维,而是把它带进页面工程。

24 篇文章最终落在六类成本

成本代表问题对应篇目
渲染边界RSC/Client、SSR/ISR、首屏接续M3-01~04、07
内容结构首页、分类、Markdown、导航辅助M3-05~10
身份安全token、BFF、会员数据、互动M3-11~14
内容生产评论审核、投稿状态机M3-15~16
体验质量搜索、通知、配置、响应式、性能M3-17~21
交付证据测试、Worker、R2、上线边界M3-22~23

页面只是这些决策的表面结果。

复用成功的部分

共享 OpenAPI 让新前台沿用后端契约,类型不必手写。文章卡片、分页、Markdown 渲染、反馈状态和请求错误模型也形成了稳定基础。

exporttypeArticleSummary=components['schemas']['ArticleSummary']exporttypeUser=components['schemas']['User']
<ArticleCard article={article} categories={tree} /> <Failure retry={() => query.refetch()} />

真正有效的复用建立在语义一致上。收藏、点赞、阅读历史虽然共用卡片,却没有被强行压成同一接口;公开请求和私有请求也没有为了“统一封装”走同一缓存通道。

返工最多的地方都与隐含假设有关

“清空缓存更安全”误伤公开文章流

会话恢复清空整个 QueryClient,导致已挂载的公开 Feed 停止工作。修正是保留public-feed,只移除私有查询。

“有重试按钮”不等于真的恢复

错误边界 reset 没有重新执行失败的服务端请求。改为完整 reload 后,故障注入才证明搜索结果恢复。

“接口测试通过”不等于按钮调用正确

退出端点本身正常,客户端适配器却省略 Authorization。浏览器路径发现问题后,才增加真实 logout 封装测试。

“两套 slug 算法写得一样”仍会分叉

目录与正文分别解析 Markdown,在重复中文和行内格式下失配。最终方案是共享同一语法树,而不是继续同步两份正则。

契约缺口不应该由前端文案掩盖

缺口当前诚实方案没有做的伪能力
无单篇收藏状态遍历收藏分页只查前 100 条却声称准确
点赞列表不分页客户端切片并记录规模边界声称网络已经分页
无阅读事件时间序列显示累计热门写“近 7 天热门”
无文章版本已发布修改后重新 pending声称公开版与编辑版并存
评论非按楼分页显示“已加载回复”声称楼内回复总数完整
无远程部署记录本地 Worker 证据声称生产已上线

产品诚实不是保守措辞,而是让界面、代码和契约说同一件事。

缓存是业务承诺,不是性能开关

// 公开内容fetch(url,{cache:'force-cache',next:{revalidate:60}})// 私有内容fetch(url,{cache:'no-store'})response.headers.set('cache-control','private, no-store')

60 秒意味着运营修改可能短暂不可见,过期首访还可能先得到旧值。no-store 则保护会员数据不被公共复用。缓存设计先回答“谁的数据、可以旧多久”,然后才是速度。

安全边界来自多层协作

浏览器:access token 仅内存 Cookie:refresh token HttpOnly + SameSite + Secure BFF:路径限制、Cookie 转换、Origin/Host 校验、no-store 后端:鉴权、角色与资源归属最终裁决

任何单层都不能独立宣布安全。客户端守卫可被绕过,Origin 校验不替代鉴权,HttpOnly 也不能消除 XSS 代表用户发请求的风险。

“完成”必须带上证据范围

pnpmgen:apipnpmtypecheckpnpmlintpnpmtestpnpmbuildpnpmbuild:cf

这些命令加上双运行时接口联调、页面冒烟、浏览器流程和缓存故障注入,证明了当前代码在本地指定环境下成立。

它们没有证明:真实手机全覆盖、生产性能 SLA、远程 R2 多节点传播、新机器零安装和正式域名 Cookie。边界写进报告,交付才可复核。

如果重新做一次,我会更早确定什么

  1. 先画公开数据、私有数据和本地状态的所有权表。
  2. 为每个路由写清新鲜度与错误语义,再选择渲染方式。
  3. 从第一天给查询 key 加身份与筛选维度。
  4. 将故障恢复纳入验收,不只做正常截图。
  5. 对缓存、认证和代理同时测试允许路径与拒绝路径。
  6. 用真实最长内容、四级分类和两个账号准备测试数据。
  7. 把本地构建、远程部署和生产指标分成三个里程碑。

哪些判断可以迁移到别的项目

判断可迁移原则
Server/Client 边界先把无需浏览器的代码留在服务端
页面渲染策略按数据所有权和新鲜度逐页决定
错误处理核心失败与辅助降级分开
状态缓存query key 必须表达身份与筛选
树形数据UI 展开属前端,数据归属查询属后端
验证每项证据只支持有限结论

下一阶段仍需面对的生产工作

正式 API 与内容 → 生产域名和 NEXT_PUBLIC_SITE_URL → 独立 Worker 与 R2 → Cookie、Origin 和缓存验收 → 发布与回退方案 → 真实用户性能与错误监测

这不是 M3 实现未完成,而是线上发布本来就有环境前提。把两者分开,既不贬低已经完成的工程,也不提前透支生产结论。

小结

公开内容站的真实成本,不在页面数量,而在它同时面对搜索引擎、游客、会员、作者、缓存和两个运行时。每一类用户和环境都带来新的状态与证据要求。

这 24 篇最终形成的不是一套 Next.js API 清单,而是一种全栈判断方式:先确定数据属于谁、何时生成、可以旧多久、失败后保留什么,再写组件和请求。框架会继续变化,这些问题不会。

延伸阅读

  • App Router 与 CSR 时代的思维差异
  • 质量门禁
  • OpenNext 部署 Cloudflare

如果这篇文章对你有帮助,欢迎订阅我的 CSDN 专栏「成为全栈」:

🔗 专栏地址:https://blog.csdn.net/fungleo/category_13204651.html

📦 本系列配套代码仓库:https://github.com/fengcms/become-a-full-stack-developer

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询