
OpenMontage 前端工程实践RSC Props 去重序列化——在客户端做数据变换避免重复网络载荷【免费下载链接】OpenMontageWorlds 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 ComponentsRSC架构下服务端组件向客户端组件传递 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.mdMinimize Serialization at RSC BoundariesImpact: 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.mdUse 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[]案例中users与users.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.mdPer-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或大型配置对象而客户端仅使用primaryColor、fontFamily等少量字段就同时触发了字段过度序列化与潜在重复引用两类问题——正确的做法是先只传最小字段集再对需要变换的数据排序、筛选、映射统一放到客户端用useMemo处理。Remotion 的CalculateMetadataFunction如 CinematicRenderer.tsx 与 TitledVideo.tsx 中的calculateTitledVideoMetadata这类纯数据推导函数同样是在消费端做派生模式的良好示范数据在靠近使用处变换跨端只传输最小必要载荷。小结一条可立即执行的自查清单数引用同一份数据是否以多个 props 传给了同一个客户端组件若是检查是否在服务端做了产生新引用的变换看类型重复的是string[]/number[]/boolean[]全量重复HIGH 影响还是object[]仅数组外壳重复LOW 影响据此决定修复优先级移变换把.toSorted()、.filter()、.map()、.slice()、[...arr]、{...obj}等操作下沉到客户端配合useMemo与toSorted()保持不可变判例外变换昂贵或客户端不需要原始数据时才在服务端传派生结果联动检查同时对照 server-serialization.md 的只传用到的字段两条规则一起落地才能把 RSC 边界上的网络载荷压到最低。【免费下载链接】OpenMontageWorlds 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),仅供参考