news 2026/9/11 16:24:31

OpenMontage 前端工程实践:RSC Props 去重序列化——在客户端做数据变换,避免重复网络载荷

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
OpenMontage 前端工程实践:RSC Props 去重序列化——在客户端做数据变换,避免重复网络载荷

OpenMontage 前端工程实践:RSC Props 去重序列化——在客户端做数据变换,避免重复网络载荷

【免费下载链接】OpenMontageWorld's first open-source, agentic video production system. 12 production pipelines, 100+ tools, 700+ agent skill and production-knowledge files. Turn your AI coding assistant into a full video production studio.项目地址: https://gitcode.com/GitHub_Trending/op/OpenMontage

导读

在 React Server Components(RSC)架构下,服务端组件向客户端组件传递 props 时,所有数据都会被序列化并内嵌进 HTML 与后续 RSC 请求中,直接影响页面体积与加载时间。本文聚焦 Vercel React 最佳实践中的一条高频规则——避免 RSC props 重复序列化server-dedup-props):先讲清 RSC 序列化"按对象引用去重、而非按值去重"的底层机制,再给出将.toSorted().filter().map()等变换下沉到客户端的正确写法,并通过string[]object[]的对比揭示不同数据类型的真实开销差异,帮助你在写服务端组件时少传数据、传对数据。

该规则来自当前仓库技能库 vercel-react-best-practices 的"服务端性能(Server-Side Performance)"章节(规则文件:server-dedup-props.md),适合任何使用 Next.js App Router、RSC 或类似服务端组件架构的 React 工程。

RSC 边界上的序列化:一切 props 都会被"打包"传输

在 RSC 架构中,服务端组件(默认无'use client'指令的组件)在服务端执行,并把渲染结果连同 props 数据序列化后下发给浏览器,用于客户端组件的水合与渲染。这意味着:

  • 所有传给客户端组件的 props 都会变成字符串内嵌进 HTML 响应和后续的 RSC 请求
  • 序列化数据的大小直接贡献于页面权重与加载时间,因此"传多少"比"怎么传"更值得精打细算。

姊妹规则 server-serialization.md(Minimize Serialization at RSC Boundaries,Impact: HIGH)从另一个角度强调了同样的原则:一个 50 字段的 user 对象如果整包下传而客户端只用 1 个字段,那么 49 个字段就是纯浪费,应当只传name={user.name}去重序列化规则是它的"量"的补充——关注同一个值被序列化了几次。

核心机制:去重按"对象引用"而非"值"

RSC→客户端的序列化系统会做一次去重,但它的判定依据是对象引用(reference),而不是值(value)

  • 同一个引用传给多个 props → 只序列化一次;
  • 新产生的引用传给多个 props → 每个引用都重新序列化一次。

由此引出一条关键推论:如果在服务端对同一个数组做.toSorted().filter().map()等变换,就会创建出新的数组引用,导致同一份数据被序列化两次——一次是原始数组,一次是变换后的副本。

规则原文给出了最典型的反模式:

// RSC: sends 6 strings (2 arrays × 3 items) <ClientList usernames={usernames} usernamesOrdered={usernames.toSorted()} />

usernames有 3 个元素时,usernames数组与usernames.toSorted()生成的数组各携带 3 个字符串,总共传输 6 个字符串;而客户端大概率只关心排序后的结果。

正确做法是只传一次,变换放到客户端

// RSC: send once <ClientList usernames={usernames} /> // Client: transform there 'use client' const sorted = useMemo(() => [...usernames].sort(), [usernames])

这样网络载荷从 6 个字符串降到 3 个;客户端侧的排序开销由浏览器承担,且通过useMemo仅在usernames引用变化时重新计算。这里刻意在客户端使用[...usernames].sort()而非usernames.sort(),是为了配合另一条规则 js-tosorted-immutable.md(Use toSorted() Instead of sort() for Immutability)——sort()会原地修改数组,可能污染 RSC 传来的 props,破坏 React 的不可变模型;现代浏览器与 Node.js 20+ 可直接使用toSorted()获得不改变原数组的排序副本。

嵌套去重行为:不同数据类型的开销差异

去重是递归生效的——数组本身及其内部元素都会参与引用去重,因此影响随数据类型而不同:

数据类型影响等级重复序列化的内容
string[]number[]boolean[]HIGH(高)数组外壳 + 内部所有原始值全部重复
object[]LOW(低)仅数组外壳重复,嵌套对象按引用去重,不会重复

用代码直观对比:

// string[] - duplicates everything usernames={['a','b']} sorted={usernames.toSorted()} // sends 4 strings // object[] - duplicates array structure only users={[{id:1},{id:2}]} sorted={users.toSorted()} // sends 2 arrays + 2 unique objects (not 4)

string[]案例中,两个数组各带'a''b'共 4 个字符串;object[]案例中,usersusers.toSorted()两个数组外壳重复了,但内部的{id:1}{id:2}两个对象因为引用相同只序列化一次,最终传输量为 2 个数组结构 + 2 个唯一对象,而非 4 个对象。

这一对比的价值在于:判断一条"去重失败"是否值得修复,要先看数据类型。对object[]来说,重复的只是数组外壳,影响有限;而对string[]/number[]/boolean[],重复的是全部内容,修复收益显著。

会破坏去重的操作清单

规则明确列出了会创建新引用、从而破坏去重的常见操作:

  • 数组.toSorted().filter().map().slice()、展开运算符[...arr]
  • 对象:展开{...obj}Object.assign()structuredClone()JSON.parse(JSON.stringify())

规则给出的正反示例进一步补充了"派生数据"与"字段摘取"两类典型场景:

// ❌ Bad <C users={users} active={users.filter(u => u.active)} /> <C product={product} productName={product.name} /> // ✅ Good <C users={users} /> <C product={product} /> // Do filtering/destructuring in client

注意第二个反例:productName={product.name}本身并不是"重复序列化",它只是多传了一个可从product中派生出的字段,与姊妹规则 server-serialization.md 的"只传客户端真正用到的字段"原则直接呼应。把筛选(filtering)和结构解构(destructuring)都移到客户端,是两条规则的共同结论。

例外情况:什么时候该在服务端传派生数据

规则给出了明确的例外:当变换本身代价高昂(expensive),或客户端根本不需要原始数据时,应在服务端传派生数据

这背后的权衡是序列化成本与计算成本的互换:

  • 默认策略:变换开销小、客户端需要原始数据 → 服务端只传原始引用,客户端自行变换(本规则的主路径);
  • 例外策略:变换是重计算(例如大规模排序、聚合、调用了服务端 API)→ 在服务端算好再传,避免把高成本计算重复分发到每个客户端;客户端只用到排序/筛选后的结果时,同样不应把完整原始数据集下传。

类似的"按需取舍"思想在该技能库的服务端章节中贯穿始终:需要跨请求缓存时可参考 server-cache-react.md(Per-Request Deduplication with React.cache(),注意React.cache()也以引用/原语相等性判定缓存命中),静态资源加载则可参考 server-hoist-static-io.md 把 I/O 提升到模块级。

在当前仓库中的实践落点

OpenMontage 仓库内的 React/Remotion 工程 remotion-composer 提供了直观的对照样本:例如 Root.tsx 中定义了大型的ThemeConfig接口与THEMES主题记录,各组合组件通过 props 消费其中的主题字段。在类似场景中,若由服务端向客户端组件整包传递THEMES或大型配置对象,而客户端仅使用primaryColorfontFamily等少量字段,就同时触发了"字段过度序列化"与"潜在重复引用"两类问题——正确的做法是先只传最小字段集,再对需要变换的数据(排序、筛选、映射)统一放到客户端用useMemo处理。Remotion 的CalculateMetadataFunction(如 CinematicRenderer.tsx 与 TitledVideo.tsx 中的calculateTitledVideoMetadata)这类纯数据推导函数,同样是"在消费端做派生"模式的良好示范:数据在靠近使用处变换,跨端只传输最小必要载荷。

小结:一条可立即执行的自查清单

  1. 数引用:同一份数据是否以多个 props 传给了同一个客户端组件?若是,检查是否在服务端做了产生新引用的变换;
  2. 看类型:重复的是string[]/number[]/boolean[](全量重复,HIGH 影响)还是object[](仅数组外壳重复,LOW 影响),据此决定修复优先级;
  3. 移变换:把.toSorted().filter().map().slice()[...arr]{...obj}等操作下沉到客户端,配合useMemotoSorted()保持不可变;
  4. 判例外:变换昂贵或客户端不需要原始数据时,才在服务端传派生结果;
  5. 联动检查:同时对照 server-serialization.md 的"只传用到的字段",两条规则一起落地,才能把 RSC 边界上的网络载荷压到最低。

【免费下载链接】OpenMontageWorld's first open-source, agentic video production system. 12 production pipelines, 100+ tools, 700+ agent skill and production-knowledge files. Turn your AI coding assistant into a full video production studio.项目地址: https://gitcode.com/GitHub_Trending/op/OpenMontage

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/11 16:23:29

IWOA-BiLSTM:改进鲸鱼算法优化双向LSTM超参

简介&#xff1a;本资源是一套面向高校科研人员与算法工程师的MATLAB时间序列预测实践代码包&#xff0c;聚焦于改进型鲸鱼优化算法&#xff08;IWOA&#xff09;与双向长短期记忆网络&#xff08;BiLSTM&#xff09;的融合建模与性能对比。资源解决了传统BiLSTM超参数调优依赖…

作者头像 李华
网站建设 2026/9/11 16:22:01

免密码进行SSH连接、Mac远程连接windows系统(拷贝本地文件)

文章目录 前言 I 免密码进行SSH连接 1.1 创建 rsa 1.2 配置 ssh config 1.3 测试连接 1.4 案例: 配置GitHub SSH keys II 远程连接windows系统。 2.1 Mac远程连接windows 2.2 windows远程连接windows 2.3 RustDesk开源远程桌面访问解决方案 III see also 移除私钥密码(Passph…

作者头像 李华
网站建设 2026/9/11 16:15:42

西门子S7-1500 PLC在中央空调控制系统中的应用

1. 中央空调控制系统概述与选型考量 中央空调系统作为现代建筑环境控制的核心设备&#xff0c;其自动化程度直接影响着能耗水平和使用体验。传统继电器控制方式存在布线复杂、故障率高、难以扩展等固有缺陷&#xff0c;而基于PLC的控制系统则完美解决了这些问题。在众多PLC产品…

作者头像 李华