这本书在讲什么
《互联网产品的客户端架构》讨论一个具体问题:同一款产品,在用户手里的那台设备上,架构应该长成什么样。
它不教 TypeScript 语法,也不比较框架的 API。它从产品事实出发,讨论端上独有的约束怎样改变渲染、数据、状态与发布四条路径,并把这些判断写成可以运行、可以回归的代码。贯穿案例是「回声」——一个同时有内容流、私信与站内下单的产品;上一套《互联网产品的服务端架构》从服务器看它,这一套从端上看它。
四卷的分工与卷二的位置
| 卷 | 主题 | 主要问题 |
|---|---|---|
| 卷一 | 端形态与体验基础 | 端上约束如何决定分层、状态、路由、数据与渲染 |
| 卷二 | 内容、社交与实时互动 | 持续流动的数据如何在端上稳定存在 |
| 卷三 | 交易、增长与平台化 | 一致性、实验与多端如何约束端上设计 |
| 卷四 | 多端、性能与架构演进 | 一套架构如何跨端演进而不失控 |
卷二处在「静态结构」与「高约束业务」之间。卷一给出的分层与状态归属,在这里被放进持续变化的数据里检验;卷二建立的断点、去重、降级判断,会在卷三被交易与增长场景进一步收紧。
卷二的结构
卷二 45 篇,由三部分组成:
- 前置 3 篇:序、前言、本目录。
- 正文 39 篇:13 章,每章 3 节。
- 附录 3 篇:实时协议速查、内容与实时排查手册、架构评审清单。
每一章对应实验工程 social-client-lab 里的一个目录,三个源文件对应三节,三个测试文件验证三节的核心断言。第 13 章把前面所有模块串成一条端到端流程,作为卷二的收口。
第 1 章 内容端架构与 social-lab
先把「信息流、详情、互动、实时」四条消费面分开,建立全书共用的坐标系;再给出实验工程第一个可运行的容器与三级验证线。这一章不涉及具体功能,只确定后面所有章节的边界。
第 2 章 信息流
信息流的核心不是「拉数据」,而是「稳定地向下滚」。本章讨论页码分页为什么在内容流里失效、复合游标如何编码、去重集合与位置保持如何配合,以及下拉、自动、回到前台三种刷新各自该不该触发。
第 3 章 内容渲染与富文本
一条内容从服务端到屏幕,要经过块模型、信任分级与布局占位三道关。本章把正文当成结构化数据而不是 HTML 字符串,讨论谁能写 HTML、白名单 sanitize 的边界,以及媒体占位如何决定 CLS。
第 4 章 媒体上传与资源
发布一条带图或带视频的内容,端上要完成分片、并发、进度、续传与格式选择。本章讨论进度语义的歧义、断点续传需要持久化什么,以及图片格式探测失败时如何逐级降级。
第 5 章 互动与乐观更新
点赞、收藏、评论都是写路径,但回滚成本不同。本章给出乐观更新的分层判据:哪些操作可以本地先行、哪些必须保草稿、哪些根本不应该乐观,以及回滚时如何只改计数不动正文。
第 6 章 实时通道选型
实时不等于 WebSocket。本章给出轮询、SSE、WebSocket 的判定表,讨论一条连接从建立到关闭的完整生命周期,以及实时不可用时产品还能退回到什么状态。
第 7 章 实时消息可靠性
断线是常态,不是异常。本章讨论重连退避如何设计、端上能保证什么程度的去重与有序,以及断线期间的消息如何补拉、如何检测空洞。
第 8 章 在线状态与「正在输入」
presence 没有真正的是非二值,只有带时间衰减的近似。本章讨论在线状态如何计算与展示、输入中状态如何节流与超时,以及状态展示的隐私边界。
第 9 章 社交关系与可见性
关注、互关、拉黑看起来是简单的关系表,端上却要处理缓存失效、可见性判断与立即生效。本章讨论关系缓存的生命周期、可见性规则在端上表达到什么程度,以及拉黑为什么必须本地先行。
第 10 章 通知与未读数
未读数是最容易出错的「小功能」。本章讨论多个来源如何合并成一个角标、多端已读以谁为准、没有推送权限时如何用打开应用补上。
第 11 章 内容审核与安全边界
审核不是服务端一个人的事。端上预检能拦什么、不能拦什么;审核态内容如何占位而不闪正文;举报链路如何留存证据。本章把审核当成一条跨端的状态机来设计。
第 12 章 搜索与发现
搜索的难点不在请求,而在输入与结果之间的竞态。本章讨论联想的防抖与乱序处理、分页与筛选状态如何保持,以及发现页的推荐位与普通信息流在架构上的差异。
第 13 章 卷二收口:social-lab 端到端
收口章不引入新概念,只验证前面的判断组合起来是否成立。发内容、互动、实时回复串成一条链路,再做断网、慢网、断线重连的故障注入,最后给出质量场景与进入卷三的风险清单。
附录
- 附录 A:实时通道协议速查。轮询、SSE、WebSocket 的能力、成本与失败模式对照。
- 附录 B:内容与实时问题排查手册。从「重复卡片」「消息丢失」「角标不准」等症状出发的排查路径。
- 附录 C:内容社交客户端评审清单。按状态归属、写路径、连接可靠性、降级顺序四组检查。
三条阅读路径
完整路径:从第 1 章顺序读到第 13 章。适合正在设计内容或社区产品、需要完整判断链的读者。
问题路径:信息流重复与位置问题看第 2 章,富文本与 XSS 看第 3 章,上传失败看第 4 章,点赞回滚看第 5 章,连接断开看第 6、7 章,未读数不一致看第 10 章。每章开头会复述所需前提。
评审路径:读第 1、5、7、13 章,再看附录 C。这四章覆盖状态归属、写路径、连接可靠性与组合验证,是评审最容易漏掉的部分。
三条路径之外,还有一种「对照路径」:先读卷一对应章节,再读卷二同一主题。例如卷一的「状态管理」对照卷二第 5 章的乐观更新,卷一的「错误与降级」对照卷二第 6、11 章。对照阅读能看清同一判断在静态结构与流动数据下的差异。
每章的三个小节通常遵循「定义问题、给出实现、处理边界」的结构。如果时间只够读一节,优先读第三节,它通常包含最多的失败复盘与边界条件。
目录中的链接都指向具体小节,可以按住 Ctrl 或 Command 在新标签页打开。章节标题下方的导语概括该章要解决的问题,不替代正文。
如果发现链接失效或章节标题与正文不一致,请以正文为准,并在反馈中注明章节号。
章节之间的关系
- 第 1 章是坐标系,所有章节引用它的四条面划分。
- 第 2 章依赖第 1 章的容器;第 3、4 章依赖内容模型,但不依赖第 2 章。
- 第 5 章依赖互动面;第 6、7、8 章形成连接链,建议连读。
- 第 9、10 章依赖第 7 章的重连与补拉;第 11、12 章相对独立。
- 第 13 章依赖前面所有章节,但每一节场景可以单独运行。
依赖不是硬约束。从中间进入时,每章开头的「前提复述」会补齐必要背景。
代码与实验工程
实验工程 social-client-lab 按章组织目录:
src/core/ 内容模型、信息流容器、分级判定
src/feed/ 游标、窗口、刷新策略
src/render/ 块模型、sanitize、占位
src/upload/ 上传编排、续传、格式策略
运行方式:
cd .local/checks/social-client-lab
node --test test/core/content-model.test.ts
代码使用 Node v26.5.0 的原生 TypeScript 类型剥离,导入路径带 .ts 后缀,零第三方依赖。标了 // file: 的代码块与工程文件逐字节一致;测试输出是真实运行结果。
数字、版本与边界
数字分三类:真实输出(测试耗时与用例数量)、案例假设(容器上限、退避间隔、合并窗口)、推导值。书中会标明属于哪一类,读者不要把假设当成实测。
本卷不承诺真机性能。iOS、Android 与小程序只讲架构建模:平台差异作为架构输入,而不是性能数字。实时只讲到「可靠增量」,不讲协议底层实现与编解码。
本卷不讨论什么
- 推荐算法与排序策略,只讲如何稳定消费。
- 富文本编辑器内核,只讲块模型与渲染边界。
- 音视频编解码与播放器内核。
- 实时协议的帧格式与拥塞控制。
- 服务端风控体系,只讲端上预检与渲染边界。
- 真机性能与平台适配。
这些主题各自值得单独成书。本卷的选择标准是:与端上架构判断直接相关,且能在 Node 环境里被验证或建模。
章节与实验目录对照
| 章 | 主题 | 实验目录 |
|---|---|---|
| 1 | 内容端架构与 social-lab | src/core/ |
| 2 | 信息流 | src/feed/ |
| 3 | 内容渲染与富文本 | src/render/ |
| 4 | 媒体上传与资源 | src/upload/ |
| 5 | 互动与乐观更新 | src/interaction/ |
| 6 | 实时通道选型 | src/realtime/ |
| 7 | 实时消息可靠性 | src/reliability/ |
| 8 | 在线状态与正在输入 | src/presence/ |
| 9 | 社交关系与可见性 | src/relations/ |
| 10 | 通知与未读数 | src/notify/ |
| 11 | 内容审核与安全边界 | src/moderation/ |
| 12 | 搜索与发现 | src/search/ |
| 13 | 卷二收口 | src/summary/ |
读完卷二你应该能回答
- 信息流出现重复卡片时,应该先查游标、去重集合还是服务端排序?三者的证据分别是什么?
- 点赞、评论、上传三种写路径,为什么回滚策略不同?判断依据是什么变量?
- 一条实时连接断开后,端上需要哪些信息才能补拉而不重复、不丢消息?
- 未读数在两端不一致时,以哪一端为准?这个选择会带来什么代价?
- 审核态内容在信息流与详情页的渲染差异,为什么必须分开处理?
- 如果要把卷二的所有模块组合成一个可上线的客户端,最先需要补的三件事是什么?
如果这六个问题都能用自己的话回答,并且能指出每个答案成立的前提,卷二的目标就达到了。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。