React 性能剖析:用 Profiler 与火焰图定位重渲染

test12026-10-226 次阅读

为什么需要 Profiler

组件树大了以后,页面卡顿往往不是某段逻辑慢,而是一次状态更新触发了不该重渲染的子树。肉眼看不出来,必须靠测量。React DevTools 的 Profiler 面板记录了每一次 commit 的开销与组件,是定位重渲染的第一抓手。

用 Profiler 组件包裹关键路径

除了 DevTools 录制,也可以在代码里用 Profiler 组件,拿到某段子树渲染的耗时与触发原因,打印到控制台辅助排查。

import { Profiler } from "react";

function onRender(
  id: string,
  phase: "mount" | "update",
  actualDuration: number,
) {
  if (actualDuration > 5) {
    console.warn(`[slow] ${id} ${phase} 用了 ${actualDuration.toFixed(2)}ms`);
  }
}

export function Dashboard({ data }: { data: number[] }) {
  return (
    <Profiler id="Dashboard" onRender={onRender}>
      <Chart data={data} />
      <Sidebar />
    </Profiler>
  );
}

看懂火焰图与高亮提交

火焰图里每一根横条是一个组件,宽度代表渲染耗时,颜色代表本次 commit 是否参与。点开某个组件,右侧会显示 Why did this render,告诉你是因为自身 state 变化、还是父组件 re-render 被顺带带下来的。找到被父级牵连的叶子节点,才是优化点。

先测量,再记忆化

不要一上来就套 useMemo。先用 Profiler 确认某段计算或某次重渲染确实昂贵,再用记忆化收口。下面这个例子里,filtered 列表只有在 items 或 keyword 变化时才重算,避免每次父组件渲染都白算一遍。

import { useMemo, useState } from "react";

export function SearchList({ items }: { items: string[] }) {
  const [keyword, setKeyword] = useState("");
  const filtered = useMemo(
    () => items.filter((i) => i.includes(keyword)),
    [items, keyword],
  );
  return (
    <ul>
      {filtered.map((i) => (
        <li key={i}>{i}</li>
      ))}
    </ul>
  );
}

关键结论

  • 性能问题先看火焰图,别凭直觉加 memo。
  • 重渲染的元凶通常是父组件 re-render 顺带拖累,而不是子组件自己慢。
  • 记忆化有成本,只在测量结果支持时使用。
T

test1

文章作者

为什么需要 Profiler 组件树大了以后,页面卡顿往往不是某段逻辑慢,而是一次状态更新触发了不该重渲染的子树。肉眼看...

分类
技术
发布时间
2026-10-22
字数
约 1412 字
阅读
6 次