













在 React 15 中,类组件的 render 方法被多次调用是一个常见现象,这主要是由 React 15 的同步渲染架构和协调(Reconciliation)机制决定的。
React 15 的渲染过程是同步且不可中断的。一旦开始渲染,就会从根组件一直递归渲染到底。在这种机制下,render 被多次执行主要有以下几个原因:
React 15 的协调算法默认是纯递归的。每当父组件 render 执行,React 就会默认生成一棵新的虚拟 DOM 树,然后全量与旧的虚拟 DOM 树进行对比(Diff)。
setState 或 props 变化而重新渲染,那么其所有子组件(默认情况下)都会执行 render,无论子组件的 props 是否真的发生了变化。setState 的同步与异步特性React 15 中的 setState 机制是导致多次渲染的主要原因:
componentDidMount)或生命周期函数中,setState 是异步批量的。如果你连续调用多次 setState,它们可能会被合并为一次 render。addEventListener)或 setTimeout/Promise 回调中调用 setState,React 15 无法进行自动批处理。每调用一次 setState,都会立即触发一次完整的 render 流程。例如:// 在 setTimeout 中
setTimeout(() => {
this.setState({ a: 1 }); // 触发一次 render
this.setState({ b: 2 }); // 触发一次 render
}, 0);
// 总共执行了 2 次 render
componentWillReceiveProps 中的 setState在 React 15 中,当父组件重绘导致子组件接收新的 props 时,会触发子组件的 componentWillReceiveProps 生命周期。如果开发者在这个钩子中调用了 setState,会导致在当前渲染流程中再调度一次更新:
setState -> 父组件 render -> 子组件 componentWillReceiveProps 触发 -> 子组件调用 setStatesetState 会标记子组件需要更新,但当前渲染流程已经在进行中,它会在此次渲染流程结束后再次发起一次新的更新流程,导致子组件又执行了一次 render。在 React 15 中,即使父组件传递的 props 内容相同,如果传递的是内联函数或对象字面量,由于 JavaScript 的引用比较特性,子组件的 shouldComponentUpdate 默认返回 true,导致子组件重新 render。
// 父组件 render 中
<Child
onClick={() => {}} // 每次父组件 render,这都是一个新的函数引用
style={{ color: "red" }} // 每次都是新的对象引用
/>
使用了 forceUpdate() 会直接跳过 shouldComponentUpdate 的生命周期检查,强制触发 render。
shouldComponentUpdateReact 15 默认没有自动的深层比较。如果组件没有实现 shouldComponentUpdate,或者没有使用 React.PureComponent(仅进行浅比较),只要父组件更新,当前组件就会无条件执行 render。
理解 React 15 的多次渲染,关键在于理解它的架构缺陷:
shouldComponentUpdate)。render 仍然可能被多次调用(例如并发模式下),但引入了更智能的批处理(unstable_batchedUpdates 变成了默认行为)和时间切片,使得不必要的 render 次数显著减少,且不再完全依赖纯递归的父传子渲染链。如果你在维护 React 15 的老项目,遇到性能问题,可以优先检查:
setTimeout、Promise 或原生事件中调用了 setState。React.PureComponent 或实现 shouldComponentUpdate。此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。