当前位置: 首页 > news >正文

React15 - 为什么React 15应用在页面渲染时会多次执行类组件的render 函数?

在 React 15 中,类组件的 render 方法被多次调用是一个常见现象,这主要是由 React 15 的同步渲染架构协调(Reconciliation)机制决定的。

React 15 的渲染过程是同步且不可中断的。一旦开始渲染,就会从根组件一直递归渲染到底。在这种机制下,render 被多次执行主要有以下几个原因:

1. 父组件渲染导致子组件连锁渲染

React 15 的协调算法默认是纯递归的。每当父组件 render 执行,React 就会默认生成一棵新的虚拟 DOM 树,然后全量与旧的虚拟 DOM 树进行对比(Diff)。

  • 如果父组件因为 setStateprops 变化而重新渲染,那么其所有子组件(默认情况下)都会执行 render,无论子组件的 props 是否真的发生了变化。
  • 这在 React 15 中被称为 “无条件的默认渲染”

2. setState 的同步与异步特性

React 15 中的 setState 机制是导致多次渲染的主要原因:

  • 生命周期内的批处理:在 React 自身管理的事件(如 componentDidMount)或生命周期函数中,setState异步批量的。如果你连续调用多次 setState,它们可能会被合并为一次 render
  • 原生事件与异步代码中:如果在原生事件(addEventListener)或 setTimeout/Promise 回调中调用 setState,React 15 无法进行自动批处理。每调用一次 setState,都会立即触发一次完整的 render 流程。例如:
// 在 setTimeout 中
setTimeout(() => {this.setState({ a: 1 }); // 触发一次 renderthis.setState({ b: 2 }); // 触发一次 render
}, 0);
// 总共执行了 2 次 render

3. componentWillReceiveProps 中的 setState

在 React 15 中,当父组件重绘导致子组件接收新的 props 时,会触发子组件的 componentWillReceiveProps 生命周期。如果开发者在这个钩子中调用了 setState,会导致在当前渲染流程中再调度一次更新:

  1. 父组件 setState -> 父组件 render -> 子组件 componentWillReceiveProps 触发 -> 子组件调用 setState
  2. 由于 React 15 是同步的,这次 setState 会标记子组件需要更新,但当前渲染流程已经在进行中,它会在此次渲染流程结束后再次发起一次新的更新流程,导致子组件又执行了一次 render

4. 内联函数与对象字面量

在 React 15 中,即使父组件传递的 props 内容相同,如果传递的是内联函数对象字面量,由于 JavaScript 的引用比较特性,子组件的 shouldComponentUpdate 默认返回 true,导致子组件重新 render

// 父组件 render 中
<ChildonClick={() => {}} // 每次父组件 render,这都是一个新的函数引用style={{ color: "red" }} // 每次都是新的对象引用
/>

5. 强制更新

使用了 forceUpdate() 会直接跳过 shouldComponentUpdate 的生命周期检查,强制触发 render

6. 未优化的 shouldComponentUpdate

React 15 默认没有自动的深层比较。如果组件没有实现 shouldComponentUpdate,或者没有使用 React.PureComponent(仅进行浅比较),只要父组件更新,当前组件就会无条件执行 render


总结:React 15 vs React 16+ 的区别

理解 React 15 的多次渲染,关键在于理解它的架构缺陷:

  • React 15:基于 Stack Reconciler(栈调和器)。渲染不可中断,任何一点状态的变动都可能引起整棵子树的重新递归渲染。性能优化完全依赖手动控制(shouldComponentUpdate)。
  • React 16+ (Fiber):引入了 Fiber 架构。渲染可以中断、分片。虽然 render 仍然可能被多次调用(例如并发模式下),但引入了更智能的批处理unstable_batchedUpdates 变成了默认行为)和时间切片,使得不必要的 render 次数显著减少,且不再完全依赖纯递归的父传子渲染链。

如果你在维护 React 15 的老项目,遇到性能问题,可以优先检查:

  1. 是否在 setTimeoutPromise 或原生事件中调用了 setState
  2. 子组件是否应该继承 React.PureComponent 或实现 shouldComponentUpdate
  3. 是否存在过深的组件嵌套导致渲染链过长。
http://www.jsqmd.com/news/530152/

相关文章:

  • 如何将openKylin配置成可以让匿名用户访问的FTP服务器(v0.1.0)
  • 颠覆式突破:SubtitleOCR让硬字幕提取效率提升300%,零基础上手智能处理全指南
  • Stable-Diffusion-v1-5-archiveWeb UI定制化:自定义CSS/快捷按钮/历史记录导出技巧
  • 破局流量焦虑:机床厂商网络推广的渠道甄选与策略重构 - 品牌推荐大师
  • DeepSeek-OCR-2效果实测:vLLM加速前后延迟对比(200ms→42ms)
  • 基础算法:差分(Difference Array)
  • XCOM 2模组管理架构深度解析:AML启动器的技术实现与优化策略
  • 20252904 2025-2026-2 《网络攻防实践》第2周作业.19766389
  • DeOldify模型轻量化探索:在STM32边缘设备上的部署可能性分析
  • 电缆生产厂家推荐哪家?2026年3月电缆生产厂家推荐名单 - 品牌2026
  • 2026年中国电缆一线品牌行业洞察:电缆标杆品牌深度解析与选购指南 - 品牌2026
  • 提供给需要学习的同学,C#读取,写入1200控制西门子V90源代码,博途V13C#源代码VS3...
  • Linux为什么要分区?
  • 博图中RTD/TC信号处理的常见问题与解决方案
  • Xenia Canary进阶指南:深度解析Xbox 360模拟器的专业配置与性能调优
  • 20254214乔若曦实验一《Python程序入门设计》
  • Zotero PDF Translate插件自动翻译失效问题系统解决方案
  • No.1091 三菱PLC和组态王组态变频器的恒压供水系统控制 我们主要的后发送的产品有
  • 西门子PLC S7-200在立体车库控制系统中的应用联系
  • 如何通过Thief-Book将IDE变成高效阅读空间:开发者碎片化时间利用指南
  • WrenAI实战指南:从环境适配到场景落地的非典型路径
  • Qwen3-Reranker效果展示:医疗问答场景中症状描述与病历文档匹配案例
  • 如何突破AI开发成本壁垒?开源社区的零成本方案
  • FinalShell最新版控制台背景DIY教程:无需VIP也能玩转个性化(附高清素材包)
  • 创作效率翻倍!用yz-bijini-cosplay快速生成同人图、角色设定参考
  • 6ES5470-7LC13西门子模拟量输出模块
  • 如何快速掌握AwesomeTTS:面向Anki用户的终极语音学习指南
  • 别再只盯着人脸识别了!聊聊STM32F103c8t6+K210方案在智能门禁中的其他可能性
  • 百度网盘下载加速完全指南:突破限制的技术原理与实战方案
  • 被低估的创意引擎:ComfyUI工作流自动化的隐藏价值挖掘