组件重构是系统性调整渲染行为,核心是确保每次渲染必要且开销可控:拆分职责、稳定props引用、固化回调与样式、按需激活渲染路径。

组件重构不是简单地重写代码,而是围绕渲染行为本身做系统性调整。核心目标是让每次渲染都真正必要,且开销可控。
拆分渲染职责,分离逻辑与视图
把一个承担数据获取、状态管理、UI渲染的“全能型”组件,拆成多个专注单一职责的子组件。比如将列表页拆为:数据加载器(只管请求和缓存)、过滤控制器(只响应筛选操作)、渲染器(纯函数式,只接收扁平化数据)。这样,筛选变化时只有控制器和渲染器更新;数据刷新时,加载器触发新数据流,但过滤器不参与重渲染。
- 渲染器组件用 React.memo 包裹,确保 props 不变就不重绘
- 过滤控制器内部状态独立,不污染列表数据结构
- 数据加载器返回稳定引用的对象或数组,避免上游因引用变化连带触发下游渲染
重构 Props 结构,消除隐式依赖
常见问题是父组件传入一个大对象,子组件只用其中一两个字段,但对象每次更新都生成新引用,导致子组件误判为“props变了”。重构时应主动解构、透传最小必要字段,或用 useMemo 提取稳定子结构。
- 避免:
{user: {id, name, avatar, settings, permissions}}整体传入 - 改为:
id={user.id} name={user.name} avatar={user.avatar} - 若必须传对象,用
useMemo(() => ({ id: user.id, name: user.name }), [user.id, user.name])保证引用稳定
替换内联表达式,固化回调与配置
JSX 中写 onClick={() => doSomething(id)} 或 style={{ opacity: visible ? 1 : 0 }},每次渲染都会新建函数或对象,破坏子组件的浅比较逻辑。重构时要把这些动态部分提前固化。
- 事件处理器统一用 useCallback 缓存,依赖项明确列出
- 样式对象用 useMemo 生成,或直接写 CSS 类名控制状态
- 列表项中需绑定索引或ID的场景,改用数据驱动的事件委托(如在父容器监听,通过 dataset 识别目标)
按需激活渲染路径,跳过空闲分支
很多组件包含条件区块(如加载中、错误态、空状态、主内容),但框架默认会为所有分支创建虚拟 DOM 节点。重构时可结合 const [phase, setPhase] = useState('loading') 显式控制当前激活路径,并让非活跃分支完全不参与 render 函数执行。
- 不用
{loading && <spinner></spinner>}{error && <errortip></errortip>}{data && <list items="{data}"></list>} - 改用
switch(phase) { case 'loading': return <spinner></spinner>; case 'error': return <errortip></errortip>; ... } - 配合 Suspense 或自定义 Hook 实现 phase 的自动流转,减少手动 setState 干扰










