- 人工智能
- 大模型
- 代码智能体
- AI Agent
- 桌面应用
- 后端
- 前端
- CLI
【免费下载链接】ZCode
ZCode 是 AI 编程工作台,提供桌面应用、浏览器界面和终端 Agent。本仓库包含客户端、后端服务、共享 UI,以及 Agent CLI 与运行时源码。
导读
本文深入解析 ZCode 仓库内.agents/skills/react-best-practices/rules/server-dedup-props.md所定义的核心规则:在 React Server Components(RSC)跨边界传递 props 时,如何利用"按对象引用去重"的序列化机制避免重复传输数据。你将掌握 RSC→Client 序列化的底层行为、不同数据类型(string[]、object[]等)的去重差异、破坏去重的常见操作清单,以及"变换放客户端"的落地写法,从而在开发 ZCode 这类重交互型 UI 时直接减小网络载荷、降低页面体积。
规则背景:RSC Props 序列化按"引用"去重,而非按"值"
在 React/Next.js 应用中,Server Components 渲染完成后,需要把传递给 Client Components 的 props 序列化,嵌入 HTML 响应以及后续的 RSC 请求中。该规则文件明确指出一个关键机制:
RSC→client serialization deduplicates by object reference, not value. Same reference = serialized once; new reference = serialized again.
即:RSC→Client 的序列化去重依据是对象引用(object reference),而不是对象的值。同一个引用只会被序列化一次;一旦你创建了新的引用,即使内容完全相同,也会被再次完整序列化一遍。
这条规则在仓库中被归类为Server-Side Performance(HIGH 优先级)分类下的规则之一(见 SKILL.md),文件头部的 metadata 标识了其基本信息:
--- title: Avoid Duplicate Serialization in RSC Props impact: LOW impactDescription: reduces network payload by avoiding duplicate serialization tags: server, rsc, serialization, props, client-components ---从 metadata 可以看到,该规则虽然影响等级标注为 LOW(因为重复传输的数据通常不大),但它的价值在于稳定减小网络载荷,属于高频、低风险的工程习惯。规则全文约 64 行(见 server-dedup-props.md),聚焦一个非常具体的性能问题,并给出了正反示例、数据类型差异与例外条款。
反例:同一份数组派生两份 props,序列化翻倍
规则的第一个反例非常直观——在 RSC 中同时把原始数组和它的排序副本作为两个 props 传下去:
// 错误:RSC 发送 6 个字符串(2 个数组 × 3 个元素) <ClientList usernames={usernames} usernamesOrdered={usernames.toSorted()} />分析:usernames是原始引用,usernames.toSorted()返回的是一个全新的数组引用。虽然两个数组的内容完全一样,但序列化器不认识"值相同",只认"引用不同",于是同一个 3 元素的字符串数组被完整发送了两遍——6 个字符串白白多传了一倍。
这种"一份数据、多个派生 props"的模式,在真实业务中极易出现:一个列表组件既需要原始顺序,又需要排序视图;一张用户卡片既传user又传user.name;一个表格既传users又传users.filter(...)的结果。这些派生操作每次都会产生新引用,触发重复序列化。
正解:RSC 只传一次,客户端再做变换
规则的推荐做法是:RSC 只传递原始引用,所有派生变换(排序、过滤、映射)放到 Client Component 内部执行:
// 正确:RSC 只发送一次 <ClientList usernames={usernames} />; // 客户端:在"use client"组件内变换 ("use client"); const sorted = useMemo(() => [...usernames].sort(), [usernames]);两点值得注意:
- 排序在客户端用
useMemo包裹,依赖项是usernames,只有当 props 引用变化时才重新排序,避免每次渲染都重复计算; - 客户端使用
[...usernames].sort()(展开 + 原地排序)而非.toSorted(),这在旧浏览器环境是可用的兼容写法。ZCode 仓库内的另一条规则 js-tosorted-immutable.md 专门讨论过.toSorted()的浏览器支持与回退方案(Chrome 110+、Safari 16+、Firefox 115+、Node.js 20+,更老环境用[...items].sort())。
从仓库的规则组织看,这与 server-serialization.md(最小化 RSC 边界序列化,只传客户端真正用到的字段)是互补的一对:前者管"传得少",后者管"不重复传"。
嵌套去重行为:数据类型决定收益大小
规则明确指出去重是递归工作的,因此重复传输的开销因数据类型而异:
string[]、number[]、boolean[]:影响 HIGH——数组引用以及内部所有原始值都会被整体重复序列化;object[]:影响 LOW——数组结构本身会重复,但内部嵌套对象因为引用相同,会被去重掉,只序列化一次。
原文档给出的对照示例:
// string[] - 全部重复 usernames={['a','b']} sorted={usernames.toSorted()} // 发送 4 个字符串 // object[] - 只重复数组结构 users={[{id:1},{id:2}]} sorted={users.toSorted()} // 发送 2 个数组 + 2 个唯一对象(不是 4 个)这条结论对评估优化优先级非常实用:如果 props 是基本类型数组,重复派生 props 的代价最高,务必移到客户端;如果是对象数组,重复的只是数组外壳,代价相对可控,但依然不值得。
破坏去重的操作清单
任何"产生新引用"的操作都会让去重失效。规则列出了常见黑名单:
- 数组:
.toSorted()、.filter()、.map()、.slice()、[...arr] - 对象:
{...obj}、Object.assign()、structuredClone()、JSON.parse(JSON.stringify())
也就是说,只要你在 RSC 中把这些方法的结果作为 props 传下去,就等于主动放弃了引用去重。尤其是JSON.parse(JSON.stringify(x))这种"深拷贝式"写法,会让整个对象树完全脱离原有引用体系,序列化开销最重。
更多正反示例:过滤与字段拆解
原文档还给出了两组常见模式的对错对比:
// ❌ 错误 <C users={users} active={users.filter(u => u.active)} /> <C product={product} productName={product.name} /> // ✅ 正确 <C users={users} /> <C product={product} /> // 过滤/解构放到客户端做这里第二组product/productName的反例尤其隐蔽:product.name虽然是简单属性读取,但在序列化层面,product与productName各自是独立的值流,product整体被序列化时已经包含了name字段,单独再传productName等于重复传输了同一条信息。这与 server-serialization.md 中的"只传客户端实际使用的字段"建议(如return <Profile name={user.name} />)需要结合判断:当一个对象整体被传递时,不要再单独传递其字段;如果客户端只用一个字段,则考虑只传该字段、不传整个对象——两者取其一即可,切忌既传整体又传局部。
例外条款:何时可以在服务端派生
规则在最后明确了一条例外:
Exception: Pass derived data when transformation is expensive or client doesn't need original.
即:当变换本身很昂贵(例如需要服务端数据集、涉及大量计算或私有数据),或者客户端根本不需要原始数据时,可以在服务端派生后再传。这是对"一律客户端变换"的务实补充——去重优化是为了省带宽,如果为了省带宽而把昂贵计算搬到每个客户端重复执行,反而得不偿失。
与仓库其他规则的协同使用
在 ZCode 仓库的 React 最佳实践规则体系中,这条规则并非孤立存在,建议配套查阅:
- server-serialization.md:最小化 RSC 边界序列化,强调"序列化数据直接决定页面重量与加载时间",只传客户端实际使用的字段;
- js-tosorted-immutable.md:
.toSorted()与.sort()的不可变性选择,以及浏览器兼容回退写法; - client-swr-dedup.md:客户端数据请求的自动去重(SWR),与 props 序列化去重分属服务端/客户端两个层面,共同控制网络载荷;
- SKILL.md 与 AGENTS.md:规则全貌与分类优先级总览,本规则位于"Server-Side Performance"分类(3.2 小节,见 AGENTS.md 中 Avoid Duplicate Serialization in RSC Props 条目);
- metadata.json:该技能包的版本、组织归属(Vercel Engineering)与参考来源。
此外,ZCode 仓库自身的大量 UI 代码中也能看到这类实践的身影——例如 packages/ui/src 下的组件与 lib 工具函数广泛使用useMemo和不可变数组操作,这正是"派生计算放客户端、依赖引用做缓存"思路的实际落地形态。
总结:一条值得固化的 RSC 性能基线
这条规则的工程价值在于它足够简单、可快速审计:
- 识别:RSC 传递给 Client Component 的 props 中,是否存在同一数据的多个派生副本(排序、过滤、映射、解构);
- 迁移:删除多余的派生 props,把变换挪进
"use client"组件并用useMemo缓存; - 权衡:基本类型数组的重复传输代价最高(HIGH),对象数组次之(LOW);仅当变换昂贵或客户端不需要原始数据时才保留服务端派生。
按此基线检查每一处 RSC→Client 边界,即可在不引入任何复杂度的前提下,稳定削减每个页面的序列化载荷——这正是 ZCode 将该规则编入react-best-practices技能包、供代码生成与审查流程自动执行的原因所在。
- 人工智能
- 大模型
- 代码智能体
- AI Agent
- 桌面应用
- 后端
- 前端
- CLI
【免费下载链接】ZCode
ZCode 是 AI 编程工作台,提供桌面应用、浏览器界面和终端 Agent。本仓库包含客户端、后端服务、共享 UI,以及 Agent CLI 与运行时源码。
相关推荐
ZCode 前端性能指南:RSC Props 重复序列化去重规则深度解析
ZCode 前端性能指南:RSC Props 重复序列化去重规则深度解析 ZCode 的 .agents/skills/react best practices
RSC Props 重复序列化规避指南:OpenMetadata 工程中的 server-dedup-props 实战解析
RSC Props 重复序列化规避指南:OpenMetadata 工程中的 server dedup props 实战解析 导读 本文聚焦 React Serv
数据目录数据血缘数据治理后端MCP 服务preguntas-entrevista-react 中的 RSC Props 重复序列化规避:按引用去重原理与实战写法
preguntas entrevista react 中的 RSC Props 重复序列化规避:按引用去重原理与实战写法 导读 在 React Server C
前端教程
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考