性能
性能优化应该由测量驱动:先定位,再修复,最后验证收益,避免“为优化而优化”。
很多团队浪费时间,是因为优化了“容易改的地方”,而不是“真正慢的地方”。测量能让你保持诚实。
性能通常来自哪里
大多数问题都能归到下面三类。先分类,再选杠杆,优化会更稳定。
- 渲染太多:状态/Context 变更触发大范围重渲染。
- 渲染太频繁:高频更新没有节流/合并。
- 渲染太慢:昂贵计算或大列表。
Profiling 工作流
- 选择可测场景(滚动/输入/导航)并稳定复现。
- 记录基线:慢在哪里、慢多少、时间花在哪。
- 一次只修一个热点,再复测验证收益。
TypeScript
import { Profiler } from 'react';
export function ProfiledArea({ children }: { children: React.ReactNode }) {
return (
<Profiler
id="Main"
onRender={(
id,
phase,
actualDuration
) => {
console.log(id, phase, actualDuration);
}}
>
{children}
</Profiler>
);
}什么时候该用 memo
当组件渲染昂贵且大多数时候 `props` 稳定时,`memo` 才更可能带来净收益。
- `memo` 有成本:比较、闭包保留与心智负担。只在“值得”的地方用。
- 稳定的 props 比 memo 更关键:避免无意义地重建对象/函数。
- 拆分状态:把高频更新限制在局部,避免牵连大子树。
TypeScript
import { memo, useMemo } from 'react';
const Row = memo(function Row({ value }: { value: number }) {
return <div>{value}</div>;
});
export function List({ items }: { items: number[] }) {
const even = useMemo(() => items.filter((x) => x % 2 === 0), [items]);
return (
<div>
{even.map((x) => (
<Row key={x} value={x} />
))}
</div>
);
}大列表
- 当列表很大且视口只显示一小段时,使用虚拟化。
- 数据天然分块时,分页/无限滚动往往比一次渲染更合适。
高频更新与体感性能
如果输入/拖拽发卡,问题往往不是“渲染太多”,而是“渲染阻塞了输入响应”。用调度工具把紧急更新保持流畅。
换句话说:把“紧急更新”(输入回显)保持即时,把“非紧急更新”(过滤大列表)延后执行。
- `useTransition`:把非紧急更新标记为可延后,让输入保持即时响应。
- `useDeferredValue`:延迟使用高频值派生出来的 UI 渲染。
TypeScript
import { useState, useTransition } from 'react';
export function SearchBox() {
const [query, setQuery] = useState('');
const [isPending, startTransition] = useTransition();
const [committed, setCommitted] = useState('');
return (
<div>
<input
value={query}
onChange={(e) => {
const next = e.target.value;
setQuery(next);
startTransition(() => setCommitted(next));
}}
placeholder="Search…"
/>
<div>{isPending ? 'Loading…' : null}</div>
<Results query={committed} />
</div>
);
}
function Results({ query }: { query: string }) {
return <div>Query: {query}</div>;
}上线检查清单
- 性能优化有测量证据:有 before/after 对比。
- 热点从源头修复(拆 state、建立 memo 边界),而不是全站乱加 memo。
- 大列表已分块/虚拟化;`pending` 期间交互保持响应。
- 导航具备 `loading`/`error`,避免瀑布请求。
延伸阅读
- Guides:状态管理
- Reference:useTransition / useDeferredValue
- Reference:Profiler / memo / useMemo / useCallback
- Explanation:并发与 Suspense