第 11 章:ClassComponent——19 的遗留支持(选讲)
选讲。React 19 还在支持 Class 组件,大量存量代码也没迁走,所以值得知道它怎么渲染。但 19 明确不再演进它,新代码别写。
先看一个极简 Class:
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = {count: 0};
}
shouldComponentUpdate(nextProps, nextState) {
return this.state.count !== nextState.count;
}
render() {
return <button onClick={() => this.setState({count: this.state.count + 1})}>
{this.state.count}
</button>;
}
}
beginWork 的 ClassComponent 分支(第 6 章那个 switch)走进 updateClassComponent,干的事和函数组件殊途同归:造出实例、跑生命周期、决定要不要重渲染、重渲染就调 render()。
先记住这一句:Class 的渲染 = 实例 + 生命周期 + 一个"要不要重渲染"的判断。那个判断的出口,和第 6 章、第 10 章 memo 用的是同一个 bailout。
一图流:updateClassComponent 流程
挂载与更新,两条路
updateClassComponent(ReactFiberBeginWork.js:1577)看 workInProgress.stateNode 里有没有实例,分两路:
首次挂载:实例是空的,先 constructClassInstance(执行 constructor,这就是你的 super(props) 和 this.state = {...} 跑的地方),再 mountClassInstance(走挂载生命周期,如 componentDidMount 的调度)。
更新:实例已存在,直接 updateClassInstance。它内部做三件事:算新 state(跑 getDerivedStateFromProps、setState 的更新队列)、调 shouldComponentUpdate 决定要不要重渲染、把 componentDidUpdate 之类的副作用安排进 commit。
shouldComponentUpdate 的背后还是 bailout
这两条路汇合到 finishClassComponent(ReactFiberBeginWork.js:1689)。关键判断在这里(1702):
if (!shouldUpdate && !didCaptureError) {
return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
}
shouldComponentUpdate 返回 false,或者你继承 PureComponent 用了默认的浅比较,走的就是第 6 章那个 bailoutOnAlreadyFinishedWork(3714),整棵子树跳过。所以别再背"Class 用 sCU 优化、函数用 memo 优化",它们底层是同一个机制,只是入口不同。
需要重渲染时,instance.render() 被调用,返回 JSX 元素,交给 reconcileChildren(第 6、7 章),之后一切照旧。
为什么是"遗留"
Class 组件能做的一切,函数组件 + Hooks 都能做,而且函数组件更简单、更好拆分。React 19 的态度很明确:保持兼容、停止演进。源码里那些 getDerivedStateFromProps、componentDidCatch 的兼容分支越来越多,都只是为了不破坏存量代码。新项目写函数组件就对了。
动手实验
- 断点看构造:在
constructClassInstance(ReactFiberBeginWork.js:1645)打断点,首次渲染 Counter,看constructor是不是真的在这里被new出来。 - 验证 sCU 就是 bailout:给 Counter 加
shouldComponentUpdate,每次更新都返回false,在bailoutOnAlreadyFinishedWork(3714)打断点,确认命中。跟第 10 章 memo 的走法对比,同一个函数。
小结
- Class 渲染 = 实例 + 生命周期 + 是否重渲染的判断,挂载/更新两条路汇到
finishClassComponent。 shouldComponentUpdate返回 false,走的就是第 6 章那个bailout,和 memo 同源。- 19 对 Class 是兼容不演进,新代码用函数组件。
- 第二部分到此收尾。下一部分 Hooks:函数组件那套
useXxx到底是挂在哪里的,第 12 章见。