服务端组件刚出现时,我容易把问题想成二选一:这个页面应该是服务端还是客户端。实际项目里更有用的单位是组件树的一小段。一个页面可以在服务端读取数据、渲染大部分内容,只把搜索框、收藏按钮和弹窗留给客户端。

没有交互需求,就先留在服务端

服务端组件可以直接访问数据库或内部接口,不需要把凭据和数据获取代码发给浏览器。它也不会增加客户端 JavaScript。文章正文、商品信息、导航数据这类“读取后展示”的内容,通常很适合作为默认起点。

需要 useStateuseEffect、浏览器 API 或事件处理时,再进入客户端。这个判断比按页面类型分类更直观:不是“详情页必须服务端”,而是“这段代码是否依赖浏览器持续参与”。

不要因为一个按钮让整页变成客户端

文章页面只有一个“复制链接”按钮时,可以把按钮单独做成客户端组件,正文仍然由服务端输出。类似地,筛选栏需要状态,不代表下方每张卡片都要成为客户端组件。服务端组件可以作为 children 传入客户端外壳,边界比想象中灵活。

客户端边界会把它引用的模块带进浏览器包。边界越靠上,需要一起发送的代码通常越多。

不过也不必为了减少几百字节把一个紧密交互区域拆得支离破碎。日期选择器、弹窗内容和触发按钮如果共享很多状态,放在一个清楚的客户端模块里,维护成本可能更低。

穿过边界的数据应该简单、可序列化

从服务端传到客户端的 props 最好是字符串、数字、数组和普通对象。数据库连接、复杂类实例和函数不能直接穿过这条边界。与其把完整记录对象全部传下去,不如只选择交互真正需要的字段,也顺便减少 HTML 中的序列化数据。

我会用三个问题做检查:数据从哪里来?交互在哪里发生?这段依赖是否真的需要发送到浏览器?答案通常能自然画出边界。遇到难拆的组件,也往往说明数据获取、展示和交互本来就混在了一起。

一个实用信号:如果只是为了调用一个点击事件,就给页面顶层加客户端标记,通常可以再往下找一层更小的边界。

服务端和客户端组件并不是性能开关,而是两种执行环境。边界画得好,既能少发一些 JavaScript,也让敏感数据和浏览器交互各自待在合适的位置。目标不是追求某一边占比最大,而是让每段代码有明确的运行理由。

← 上一篇下一篇:Docker 清单 →