React Profiler 排查:看到重渲染,还要知道谁点的火
一、Profiler 不是用来截图发群里的
React 性能排查时,Profiler 很有用。它能看到组件渲染次数、耗时和提交阶段。但很多人只截一张火焰图,说“这里重渲染了”。这还不够。重渲染是结果,不是原因。真正要找的是谁触发了更新,更新是不是必要。
没有原因分析,优化就会变成乱贴memo。贴多了,代码复杂,收益还不一定稳定。Profiler 的正确打开方式,是从交互开始录制,再沿着更新链路找触发源。
二、把一次交互拆成触发、渲染和提交
一次 React 更新通常包含事件触发、状态变化、渲染计算和 DOM 提交。Profiler 主要看到后两段,前两段还需要结合代码和日志。
flowchart TD A[用户交互] --> B[事件处理] B --> C[状态更新] C --> D[组件渲染] D --> E[提交 DOM] D --> F[Profiler 火焰图] C --> G[触发源定位]只看火焰图,很容易盯着最慢组件开刀,却忽略上游状态设计有问题。
三、先标记交互,再录制数据
可以在关键交互里打 mark,和性能数据对齐。
function onFilterChange(value: string) { performance.mark("filter-change:start"); setFilter(value); requestAnimationFrame(() => { performance.mark("filter-change:end"); performance.measure("filter-change", "filter-change:start", "filter-change:end"); }); }Profiler 负责看渲染,Performance API 负责标记交互时间。两边合起来,才能知道用户点一下到底花在哪里。
四、优化顺序要从状态边界开始
如果一个输入框变化导致整个页面重渲染,先别急着 memo 子组件。先问状态是不是放太高了,Context 是不是包太大了,派生数据是不是每次都重新计算。
React.memo适合稳定 props 的纯展示组件。props 每次都是新对象,memo 也救不了。useMemo和useCallback不是免费午餐,滥用会让代码更难读。
最后,优化要复测。Profiler 前后对比提交耗时、渲染次数和交互延迟。没有复测,就不知道改动是不是心理安慰。
还要记录 props 变化原因。很多重渲染来自父组件每次创建新对象、新函数。可以在开发环境加 why-did-you-render 或自定义日志,标出哪个 prop 变了。只看组件重渲染次数,不看 prop diff,定位会慢很多。
列表场景要特别小心。虚拟列表、筛选、排序都会放大重渲染成本。优化时先确认 key 是否稳定、item 组件是否纯净、滚动时有没有触发布局抖动。React 性能问题经常不是 React 本身,而是状态和 DOM 结构设计乱了。
异步数据也会触发多次提交。loading、data、error 分开 setState,可能让一次请求产生多次渲染。可以合并状态更新,或者用 reducer 表达请求状态机。Profiler 里看到连续提交时,先查状态更新是不是被拆碎了。
最后,开发环境和生产环境要分开看。React 开发模式会有额外检查,StrictMode 也会放大某些行为。定位趋势可以用开发环境,最终数据必须用生产构建。
五、总结
React Profiler 能告诉你哪里重渲染,但不能自动告诉你为什么。排查要从用户交互开始,结合状态更新、火焰图和性能标记定位触发源。优化顺序先看状态边界,再看 memo。性能排查别靠感觉,数据会比直觉诚实。