前端系统设计 - 动态墙的非功能需求(性能)

2026年6月2日

💎 加入 E+ 成長計畫 與超過 1000+ 位工程師一同在社群成長,並獲得更多深度的軟體前後端學習資源

在前端系统设计题目中,动态墙(feed)是非常高频出现的题目之一。动态墙是各类社交媒体的标配,从 Facebook 到 Twitter,再到现在原本以电子报平台起家的 Substack,各个平台都有类似动态墙的机制。

从前端的角度来看,动态墙似乎并没有什么特别好设计的,甚至许多前端入门课程,都会练习实现简易版 Twitter(Simple Twitter)。

在看这种“功能面看似简单”的设计主题时,区分资深与否的关键点会是非功能需求(non-functional requirement)。因此这篇文章会聚焦在动态墙前端最重要的性能与可扩展性,可以如何做好相关设计。我们会拆成两篇文章来谈,这篇会专注在性能的设计。

性能(performance)

对于动态墙来说,最重要的非功能需求莫过于性能。举例来说,自从 Twitter 被收购改成 X 后,外界对产品稳定性与加载体验的讨论变多,在 X 上可以看到非常多对于 X 加载性能的抱怨。当进入网站中需要等四、五秒后才能看到帖子,即使功能没有坏掉,也会造成用户的不满,甚至导致用户流失。

在后端系统设计中,有许多讨论如何让帖子更快送到前端的内容;不过除了后端,前端设计上也有许多能让性能加快的地方,以下让我们逐一讨论。

渲染模式选择

当用户进入页面,要看到画面前,前端会需要先渲染内容出来。先前我们在《前端渲染模式的选择》一文有谈过不同的渲染模式选择(如果不熟,推荐先回顾该文后再往下读)。大家觉得在动态墙这类功能上,应该要用 CSR 还是要用 SSR 来渲染呢?

在 2020 年左右,Meta 曾在《Rebuilding our tech stack for the new Facebook.com》技术文中分享,他们把 2004 年开始用 PHP 写的 SSR,用 React 重构成 CSR 的应用。而在这个迁移的过程中,最大的挑战就是启动性能(也就是用户进入网页后有多快能看到内容)。这是因为 CSR 的做法,会先把 JavaScript 下载到前端后执行才会渲染。

现代的前端(包含 Meta 旗下的产品)都会把前端代码打包(如果对前端打包不熟,推荐回顾《前端打包工具(bundler)是什么?为什么要用?》一文),如果打包产物越大,前端要下载的时间就越长。为了避免这个问题,Meta 团队通过代码分割(Code Splitting)把打包产物拆成三个包,第一个是只有极少量的骨架屏包,让画面能渲染最基本的骨架。后面的第二个包,才会有实际的动态墙内容(第三个包则是那些不会在画面一开始出现的东西,例如要点击才会展开的菜单组件)。

但是后来业界(包含 Meta 开源的 React)都逐渐转向重新探索 SSR,借此让用户一开始能更快拿到内容呈现所需的 HTML。也因此,现在多数的动态墙在网页前端,目前都逐渐往混合模式的方向走。具体来说,会在最开始使用 SSR 搭配 hydration,后续由 CSR 接管。这是因为对动态墙来说,如果要能够越快展示内容,SSR 在服务器端先渲染好再传给前端会比较快。

举例来说,先前 Twitter 的官方技术文《Improving performance on twitter.com》就有谈到,Twitter 采用 SSR 的方式在服务器端就渲染好内容,这样能加快展示推文给用户看到。

除了加速展示外,Twitter 团队选择服务器端渲染的技术决策考量重点之一在于,如果在客户端渲染,等于渲染的性能不在开发团队的掌控中;如果用户使用比较旧款的设备,客户端的渲染就会花比较多时间。但如果渲染主要发生在服务器端,团队至少能在自己可控的基础设施上优化渲染路径,而不是完全受限于用户设备性能,这样能确保在渲染这段都有足够好的性能。

但如果只输出 SSR 的 HTML,而没有后续 JavaScript 接管,帖子虽然能先被看见,却很难提供现代社交网站需要的实时点赞、评论、分享、展开菜单等互动。因此在 SSR 把渲染好的 HTML 传到前端后,还需要再 hydration 让网页能够互动。由 CSR 接管后,动态墙将能提供高互动性,同时也能支持分页(pagination)等对性能有帮助的方法。

渐进式加载(progressive loading)

除了通过 SSR 搭配 hydration,渐进式加载也是在设计上可以协助加快加载速度的方法。所谓的渐进式加载,是指不在一开始就加载所有内容;先加载立即需要的,如果不是立即会显示在画面上的就延后加载。这种做法能够确保最开始加载的量小,借此让速度提高。在动态墙的设计上,有好几个能够运用上渐进式加载的地方。

首先,前面谈到目前业界会采用 SSR 搭配 hydration 的渲染方式,在 SSR 的部分,可以进一步采取流式(streaming)的形式(我们在《前端渲染模式的选择》一文中也有介绍,推荐回顾)。事实上,目前 Meta 旗下的社交媒体,动态墙都是采用这种方式。现在进入 facebook.com 的网站中,开启开发者工具并查看 Network 标签页,然后点击 Doc,会能看到进入画面时拿到的 HTML 文件。

这时观察这个 HTML,会发现里面的内容会不断增加,因为内容是通过流的方式不断送入。具体来说,使用流式 SSR,可以不用等到所有内容都在服务器端渲染完成才回传给前端,而是可以分区块渲染,每渲染好一个区块就回传。一般来说,用户一开始只会先看到最上面的区块,所以先回传该区块,能让感知加载速度变快(对这个实现感兴趣,推荐参考 React Conf 的这个演讲)。

更进一步说,目前多数的动态墙都会有骨架屏(skeleton loading),在还没有渲染实际帖子前,先把帖子的骨架展示出来(如下方截图),这会比用加载时常见的旋转图标有更好的用户体验。所以从流的角度来看,可以在最开始先把渲染好的骨架屏传给前端来展示,等到实际渲染完帖子后再传帖子给前端。

这种设计有个需要注意的问题,假如今天用户的网络速度很快,一开始如果展示骨架屏,然后拿到实际帖子后切换,很可能会出现一闪而过的画面,这样用户体验反而不好。所以一般会搭配时间判断,在骨架屏的 CSS 中设置透明度,然后只有在例如一定的毫秒数后才会淡入。如果帖子能在这个时间前渲染出来,则不会看到还没淡入的骨架屏,借此避免一闪而过的烦人 UX 体验。

阅读更多

除了上面提到的点,动态墙的前端还有其他能做性能优化的面向,我们在 E+ 成长计划中的主题文,有完整的版本。对更深入了解这个主题,以及其他前后端开发、软件工程、AI 工程主题感兴趣的读者,欢迎加入 E+ 成长计划一起成长(链接)。

🧵 如果你想收到最即時的內容更新,可以在 FacebookInstagram 上追蹤我們