尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Cal.com 渲染性能优化:把静态 JSX 提升到组件外部,避免每次渲染重复创建(rendering-hoist-jsx)

Cal.com 渲染性能优化:把静态 JSX 提升到组件外部,避免每次渲染重复创建(rendering-hoist-jsx) Cal.com 渲染性能优化把静态 JSX 提升到组件外部避免每次渲染重复创建rendering-hoist-jsx【免费下载链接】cal.diyScheduling infrastructure for absolutely everyone.项目地址: https://gitcode.com/GitHub_Trending/ca/cal.diy本文以 Cal.com 仓库内置的 Vercel React 最佳实践规则rendering-hoist-jsx提取静态 JSX 元素避免重复创建为核心讲解把不变的 JSX 提升到组件函数外部这一模式的工作原理、正确与错误写法对比、适用边界以及它与 React Compiler 自动优化的关系。读完后你可以判断在组件重渲染时哪些元素可以安全外提并对照仓库中的规则文档与 Next.js 配置验证当前项目是否仍需要手动提升。规则定位一条 LOW 影响的渲染优化规则该规则存放在 Vercel React 最佳实践技能目录中原始文件为 rendering-hoist-jsx.md。文件头部的元信息明确了它的定位元字段值含义titleHoist Static JSX Elements提升静态 JSX 元素impactLOW影响等级为低收益存在但优先级低于消除瀑布流、压缩包体等 CRITICAL 级规则impactDescriptionavoids re-creation核心收益是避免元素被重复创建tagsrendering, jsx, static, optimization属于渲染rendering类别的静态 JSX 优化根据 SKILL.md 的分类表rendering-前缀的规则整体属于Rendering PerformanceMEDIUM优先级而具体到本条规则其自身影响标注为 LOW——它解决的是每次组件重渲染时白白重新执行 JSX 求值、生成新的元素对象这一类可避免的浪费而非阻塞渲染或包体积问题。在完整编译版指南 AGENTS.md 中这条规则与同类渲染规则并列出现在6. Rendering Performance一节例如6.3 Hoist Static JSX Elements。同一节还包括 rendering-animate-svg-wrapper.md给 SVG 的 wrapper div 做动画以启用硬件加速和 rendering-svg-precision.md降低 SVG 坐标精度三条规则经常在实际组件中配合使用。为什么要在组件外部提升静态 JSXJSX 在编译后本质上是函数调用如jsx()/jsxs()/createElement()。只要 JSX 写在组件函数体内组件每次重渲染都会重新执行这些调用生成全新的 React 元素对象——哪怕这次渲染中该 JSX 的内容一个字符都没有变。规则原文给出的错误示例正是这种情况LoadingSkeleton内部的骨架屏 div 是完全静态的但每次Container重渲染时都会重新创建元素错误写法每次渲染都重新创建元素function LoadingSkeleton() { return div classNameanimate-pulse h-20 bg-gray-200 / } function Container() { return ( div {loading LoadingSkeleton /} /div ) }正确写法模块级常量复用同一个元素对象const loadingSkeleton ( div classNameanimate-pulse h-20 bg-gray-200 / ) function Container() { return ( div {loading loadingSkeleton} /div ) }两个写法的差别在于元素创建时机错误写法中LoadingSkeleton组件函数体里的 JSX 在每次调用LoadingSkeleton时都会重新求值正确写法中loadingSkeleton是模块顶层的const常量JSX 只在模块加载时求值一次之后每次渲染都只是引用同一个元素对象。引用稳定性带来的连锁收益当元素引用稳定相同时若它被传递到子组件作为 props配合React.memo或 React 的协调机制可以减少无意义的子树 diff 与重渲染。对于骨架屏、占位符、装饰性图标这类高频渲染路径 内容不变的节点收益最为典型。适用对象的共同特征JSX 中不含任何 props、state、闭包变量的引用即它在组件的整个生命周期内都不会变化。一旦 JSX 依赖了组件内的值如user.name、className{isOpen ? a : b}就不能提升到外部否则所有用户会看到同一份陈旧数据。从源码结构看Cal.com 的 Web 应用中已存在大量模块级const常量的写法例如 HostLocations.tsx 中在组件外定义的buildHostLocation、annotateOptionsForHost等辅助函数与常量说明把不随渲染变化的逻辑与数据移出组件函数体是该项目前端代码的常见组织习惯将同一思想推广到 JSX 元素本身就是本条规则的做法。典型收益场景大型静态 SVG 节点规则文档特别强调了一个最典型的适用场景This is especially helpful for large and static SVG nodes, which can be expensive to recreate on every render. 这对大型且静态的 SVG 节点尤其有帮助因为它们每次渲染都要重新创建代价高昂。一个包含几十个path、circle的 logo 或图表 SVG若写在一个重渲染频繁的组件体内每次状态变更都会重新执行整段 JSX 的编译产物调用、重新构造几十上百个元素对象而产出的 DOM 结构却完全相同。将其提升为模块级常量后JSX 求值只发生一次后续渲染零成本复用元素对象引用稳定便于 React 在协调阶段跳过无变化的子树组件函数体更短重渲染的热路径hot path上执行的工作更少。Cal.com 仓库内 SVG 资源非常多例如apps/web/public/下有数百个 SVG 图标以及next.config.ts中专门配置的/icons/sprite.svg图标雪碧图重写与一年期不可变缓存头这类静态图形资产在组件内以行内 JSX 形式出现时正是本规则的适用对象。边界与限制什么时候不要手动提升1. 依赖组件状态的 JSX 不能外提只有完全不引用组件作用域的 JSX 才是静态的。{loading loadingSkeleton}中的条件判断留在组件内部被复用的只是loadingSkeleton这个元素本身如果条件里用到的值会变条件本身必须留在渲染函数内。2. React Compiler 启用后手动提升变得不必要规则文档末尾给出了明确的适用前提说明If your project has React Compiler enabled, the compiler automatically hoists static JSX elements and optimizes component re-renders, making manual hoisting unnecessary. 如果项目启用了 React Compiler编译器会自动提升静态 JSX 元素并优化组件重渲染手动提升就不再必要。React Compiler 会静态分析组件函数自动完成把纯计算与不变 JSX 提到合适的作用域、为跨渲染值插入 memoization 缓存这类变换等价甚至超越人工 hoist。就当前 Cal.com 仓库而言可以确认的事实是apps/web/next.config.ts 中没有配置 React Compiler 相关的 Babel 插件配置了output、serverExternalPackages、experimental.optimizePackageImports: [calcom/ui]、transpilePackages、modularizeImports、重写与头信息等但无 compiler 插件项即 Web 应用目前仍走手动优化路线本条规则在该仓库中仍然有效。yarn.lock 中确实存在babel-plugin-react-compiler条目但仅作为其他依赖包声明的传递依赖出现peer/optional 依赖行并非本项目在next.config.ts或babel配置中主动启用的编译选项。因此对该仓库的前端代码手动识别静态 JSX 并提升到模块级常量依然是可落地的优化手段若未来项目正式接入 React Compiler则此类手动改造的必要性会下降但作为阅读代码时的模式识别能力仍然有用你能看懂编译器生成的等价变换。与同族渲染规则的配合在 SKILL.md 的6. Rendering Performance (MEDIUM)清单中本条与以下规则相邻实践中经常组合出现规则文件要点与 hoist-jsx 的协同rendering-animate-svg-wrapper.md很多浏览器对 SVG 元素的 CSS3 动画没有硬件加速应把animate-*类放到包裹 SVG 的div上对被提升为模块级常量的静态 SVG做动画时动画类同样应加在 wrapper 上rendering-svg-precision.md降低 SVG 坐标精度可减小文件体积如用npx svgo --precision1 --multipass icon.svg自动化提升出的静态 SVG 体积越大求值成本越高先压缩再提升收益叠加rendering-conditional-render.md条件渲染优先用三元表达式而非避免把0等真值之外的值渲染进 DOM本规则示例{loading loadingSkeleton}之外的真实业务中条件写法要与本规则保持一致落地清单在 Cal.com 这类大型 React/Next.js 代码库中应用本规则可以按以下顺序检查定位候选找出在重渲染频繁的组件列表行、日历单元格、表单步骤组件中返回大段不含任何 props/state 引用的 JSX 的组件或子树验证纯静态确认该 JSX 不引用组件函数作用域内的任何标识符props、state、hook 返回值、闭包变量提升为模块级常量将其移入文件顶部const xxx (div.../div)保持命名语义化如loadingSkeleton、emptyStateIcon保持条件在内部{loading xxx}一类的条件判断继续留在组件渲染体内核对编译器配置查看 apps/web/next.config.ts 等构建配置中是否已接入 React Compiler——当前仓库未启用手动提升仍然有效若未来启用此类改动可交由编译器自动完成。小结rendering-hoist-jsx的价值可以概括为一句话让永远不变的 JSX只被创建一次。它的适用前提清晰JSX 纯静态、组件会重渲染、未启用 React Compiler收益虽为 LOW 级却在大型 SVG、骨架屏、空状态提示等高频渲染场景中最容易命中。该规则完整收录于 rendering-hoist-jsx.md并作为 Vercel 45 条 React/Next.js 性能规则之一收录在 SKILL.md 索引与 AGENTS.md 完整指南中可作为代码评审时的对照清单条目使用。【免费下载链接】cal.diyScheduling infrastructure for absolutely everyone.项目地址: https://gitcode.com/GitHub_Trending/ca/cal.diy创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表