2020-07-01 06:45:23
在给定Demo中,点击Parent组件的div触发更新时,Son组件不会打印“child render!”,因为其命中了React的bailout逻辑,跳过了重新渲染。
React组件重新渲染的核心条件React组件是否重新渲染取决于Fiber树的构建过程,每个组件的Fiber节点通过以下两种方式之一创建:
当组件同时满足以下4个条件时,会进入bailout逻辑,避免重新渲染:
props未变化:
默认情况下,oldProps === newProps(严格相等)。由于JSX是React.createElement的语法糖,每次渲染生成的props对象是新的引用,即使内容未变,引用也不同。
若组件使用PureComponent或React.memo,则会对props的每个属性进行浅比较,而非严格引用比较。
context未圆氏变化:
组件未使用context,或其context.value未发生变化。
组件类型未变化:
更新前后的fiber.type相同(如函数组件Son未变为其他类型)。
无待处理的更新或优先级不匹配:
组件自身未触发更新(如setCount在Parent中调用),且当前更羡腔培新的优先级与组件关联的优先级不一致。
初始渲染:
ReactDOM.render(<App/>, rootEl)触发根节点RootFiber的渲染。
App、Parent、Son组件依次渲染,生成初始Fiber树。
点击触发更新:
Parent组件的setCount(count + 1)触发状态更新,标记Parent对应的Fiber节点为需要更新。
更新阶段的bailout判断:
RootFiber和App:
两者均未直接触发更新,且满足bailout的4个条件(props、context、type、优先级未变),因此复用上一次的Fiber节点,跳过渲染。
Parent:
由于调用了setCount,不满足条件4,进入render逻辑,重新执行函数体并返回JSX。
Son:
Son作为Parent的props.children传递,其JSX对象在App和Parent的bailout过程中被复用,导致Son的props引用未变。
同时满足bailout的其余条件(无context、类型未变、无自身更新),因此跳过渲染。
props引用的稳定性:Demo中Son的JSX通过props.children传递,而App和Parent的bailout保证了children的引用未变,从而满足oldProps === newProps。若直接渲染<Son/>(如Parent返回<div><Son/></div>),每次渲染会生成新的JSX对象,导致props引用变化,触发Son重新渲染。
优先级与更新标记:只有直接触发更新的组件(如Parent)会进入render逻辑,其子组件若满足bailout条件则会跳过渲染。
React通过严格的bailout条件优化渲染性能,避免不必要的组件更新。理解以下核心逻辑有助于优化应用: