搜索
首页web前端js教程react组件拆分的重要性

react组件拆分的重要性

Feb 20, 2021 am 11:23 AM
react组件

react组件拆分的重要性

前言:

React 是一个用于构建用户界面的 JAVASCRIPT 库。主要用于构建UI,很多人认为 React 是 MVC 中的 V(视图)。

react采用虚拟DOM技术减少Javascript与真正DOM的交互,提升了前端性能;采用单向数据流机制,父组件通过props将数据传递给子组件,这样让数据流向一目了然。

一旦组件的props或则state发生改变,组件及其子组件都将重新re-render和vdom-diff,从而完成数据的流向交互。但是这种机制在某些情况下比如说数据量较大的情况下可能会存在一些性能问题。下面就来分析react的性能瓶颈,并用结合着react-addons-perf工具来说明react组件拆分的重要性。

react性能瓶颈

要了解react的性能瓶颈,就需要知道react的渲染流程。它的渲染可以分为两个阶段:

初始组件化
该阶段会执行组件及其所有子组件的render方法,从而生成第一版的虚拟dom。

组件更新渲染。
组件的props或者state任意发生改变就会触发组件的更新渲染。默认情况下其也会执行该组件及其所有子组件的render方法获取新的虚拟dom。

我们说的性能瓶颈指的是组件更新阶段的情况。

react组件更新流程

通过上面分析可以知道组件更新具体过程如下:

执行该组件及其所有子组件的render方法获取更新后的虚拟DOM,即re-render,即使子组件无需更新。

然后对新旧两份虚拟DOM进行diff来进行组件的更新

在这个过程中,可以通过组件的shouldComponentUpdate方法返回值来决定是否需要re-render。

react的整个更新渲染流程可以借用一张图来加以说明:

434691a0c5ee658621e2271e4283ca0.png

默认地,组件的shouldComponentUpdate返回true,即React默认会调用所有组件的render方法来生成新的虚拟DOM, 然后跟旧的虚拟DOM比较来决定组件最终是否需要更新。

react性能瓶颈

借图说话,例如下图是一个组件结构tree,当我们要更新某个子组件的时候,如下图的绿色组件(从根组件传递下来应用在绿色组件上的数据发生改变):

c29cd4d86f70cef840314dc31b3273c.png

理想情况下,我们只希望关键路径上的组件进行更新,如下图:

66759c0b456d567d280464c15b89312.png

但是,实际效果却是每个组件都完成re-render和virtual-DOM diff过程,虽然组件没有变更,这明显是一种浪费。如下图黄色部分表示浪费的re-render和virtual-DOM diff。

e5ffd3a2b7b4dc2bc2ec0a8c0013c70.png

根据上面的分析,react的性能瓶颈主要表现在:

对于props和state没有变化的组件,react也要重新生成虚拟DOM及虚拟DOM的diff。

用shouldComponentUpdate来进行性能优化

针对react的性能瓶颈,我们可以通过react提供的shouldComponentUpdate方法来做点优化的事,可以有选择的进行组件更新,从而提升react的性能,具体如下:

shouldComponentUpdate需要判断当前属性和状态是否和上一次的相同,如果相同则不需要执行后续生成虚拟DOM及其diff的过程,否则需要更新。

(学习视频分享:javascript视频教程

具体可以这么显示实现:

shouldComponentUpdate(nextProps, nextState){   return !isEqual(nextProps, this.props) || !isEqual(nextState, this.state)
}

其中,isEqual方法为判断两个对象是否相等(指的是其对象内容相等,而不是全等)。

通过显示覆盖shouldComponentUpdate方法来判断组件是否需要更新从而避免无用的更新,但是若为每个组件添加该方法会显得繁琐,好在react提供了官方的解决方案,具体做法:

方案对组件的shouldComponentUpdate进行了封装处理,实现对组件的当前属性和状态与上一次的进行浅对比,从而决定组件是否需要更新。

react在发展的不同阶段提供两套官方方案:

PureRenderMin
一种是基于ES5的React.createClass创建的组件,配合该形式下的mixins方式来组合PureRenderMixin提供的shouldComponentUpdate方法。当然用ES6创建的组件也能使用该方案。

6bbe24588d612858d6b5ded49f0a1ab.png

PureComponent
该方案是在React 15.3.0版本发布的针对ES6而增加的一个组件基类:React.PureComponent。这明显对ES6方式创建的组件更加友好。

94839a4a9f12a40019e2ceeb7bd3b79.png

需要指出的是,不管是PureRenderMin还是PureComponent,他们内部的shouldComponentUpdate方法都是浅比较(shallowCompare)props和state对象的,即只比较对象的第一层的属性及其值是不是相同。例如下面state对象变更为如下值:

ce5efacefcc4c31fe218961596fc6f7.png

因为state的value被赋予另一个对象,使nextState.value与this.props.value始终不等,导致浅比较通过不了。在实际项目中,这种嵌套的对象结果是很常见的,如果使用PureRenderMin或者PureComponent方式时起不到应有的效果。

虽然可以通过深比较方式来判断,但是深比较类似于深拷贝,递归操作,性能开销比较大。

为此,可以对组件尽可能的拆分,使组件的props和state对象数据达到扁平化,结合着使用PureRenderMin或者PureComponent来判断组件是否更新,可以更好地提升react的性能,不需要开发人员过多关心。

组件拆分

组件拆分,在react中就是将组件尽可能的细分,便于复用和优化。拆分的具体原则:

尽量使拆分后的组件更容易判断是否更新

这不太好理解,举个例子吧:假设我们定义一个父组件,其包含了5000个子组件。有一个输入框输入操作,每次输入一个数字,对应的那个子组件背景色变红。

44532de6ea1942d335693e58e306b66.png

本例中,输入框组件和列表子组件有着明显的不同,一个是动态的,输入值比较频繁;一个是相对静态的,不管input怎么输入它就是5000项。输入框每输入一个数字都会导致所有组件re-render,这样就会造成列表子组件不必要的更新。

可以看出,上面列表组件的更新不容易被取消,因为输入组件和列表子组件的状态都置于父组件state中,二者共享;react不可能用shouldComponentUpdate的返回值来使组件一部分组件更新,另一部分不更新。 只有把他们拆分为不同的组件,每个组件只关心对应的props。拆分的列表组件只关心自己那部分属性,其他组件导致父组件的更新在列表组件中可以通过判断自己关心的属性值情况来决定是否更新,这样才能更好地进行组件优化。

尽量使拆分组件的props和state数据扁平化

这主要是从组件优化的角度考虑的,如果组件不需过多关注性能,可以忽略。

拆分组件之所以扁平化,是因为React提供的优化方案PureRenderMin或者PureComponent是浅比较组件的props和state来决定是否更新组件。

上面的列表组件中,this.state.items存放的是对象数组,为了更好的判断每项列表是否需要更新,可以将每个li列表项单独拆分为一个列表项组件,每个列表项相关的props就是items数组中的每个对象,这种扁平化数据很容易判断是否数据发生变化。

组件拆分的一个例子

为了这篇文章专门写了一个有关添加展示Todo列表的事例库。克隆代码到本地可以在本地运行效果。

该事例库是一个有着5000项的Todo列表,可以删除和新增Todo项。该事例展示了组件拆分前和拆分后的体验对比情况,可以发现有性能明显的提升。

下面我们结合react的性能检测工具react-addons-perf来说明组件拆分的情况。

拆分前的组件TodosBeforeDivision的render部分内容如下:

ee964b00e7c7b6529b6046a71fd82f1.png

组件拆分前,输入框输入字符、增加todo或者删除todo项可以看出有明显的卡顿现象,如下图所示:

b8de8821f9d598a49c05248efbb36cc.png

为了弄清楚是什么原因导致卡顿现象,我们使用chrome的devTool来定位,具体的做法是使用最新版的chrome浏览器的Performance选项来完成。先点击该选项中的record按钮开始记录,这时我们在组件输入框输入一个字符,然后点击stop来停止记录,我们会看到组件从输入开始到结束这段时间内的一个性能profile。

8104de499586fabf26f031dc4a5f229.png

从图可以看出我们在输入单个字符时,输入框的input事件逻辑几乎占据整个响应时间,具体的处理逻辑主要是react层面的batchedUpdates方法批量更新列表组件,而不是用户自定义的逻辑。

那么,批量更新为啥占据这么多时间呢,为了搞清楚原因,我们借助基于react-addons-perf的chrome插件chrome-react-perf,它以chrome插件的形式输出分析的结果。

使用该插件需要注意一点的是:

chrome-react-perf插件的使用需要在项目中引入react-addons-perf模块,并必须将其对象挂载到window全局对象的Perf属性上,否则不能使用。

在devTool工具中选择Perf选项试图,点击start按钮后其变成stop按钮,在组件输入框中输入一个字符,然后点击Perf试图中的stop按钮,就会得出对应的性能试图。

122fbadeb10b6f09a9d3ea984593c18.png

上图提供的4个视图中,Print Wasted对分析性能最有帮组,它表示组件没有变化但是参与了更新过程,即浪费了re-render和vdom-diff这一过程,是毫无意义的过程。从图可以看出:TodosBeforeDivision和TodoItem组件分别浪费了167.88ms、144.47ms,这完全可以通过拆分组件避免的开销,这是react性能优化重点。

为此我们需要对TodosBeforeDivision组件进行拆分,拆分为一个带有input和button的动态组件AddTodoForm和一个相对静态的组件TodoList。二者分别继承React.PureComponent可以避免不必要的组件更新。

ad033c0ae6ebd7725c3a889495a96c2.png

其中TodoList组件还需要为每项Todo任务拆分为一个组件TodoItem,这样每个TodoItem组件的props对象为扁平化的数据,可以充分利用React.PureComponent来进行对象浅比较从而更好地决定组件是否要更新,这样避免了新增或者删除一个TodoItem项时,其他TodoItem组件不必更新。

52d4c5987358885831ac22ea32ad830.png

这样拆分后的组件,在用上面的性能检测工具查看对应的效果:

e1a3dc70d14495014df40c7725fd869.png

从上面的截图可以看出,拆分后的组件性能有了上百倍的提升,虽然其中还包含一些其他优化,例如不将function在组件属性位置绑定this以及常量对象props缓存起来等避免每次re-render时重新生成新的function和新的对象props。

总的来说,对react组件进行拆分对react性能的提升是非常重要的,这也是react性能优化的一个方向。

相关推荐:react教程

以上是react组件拆分的重要性的详细内容。更多信息请关注PHP中文网其他相关文章!

声明
本文转载于:博客园。如有侵权,请联系admin@php.cn删除
Python vs. JavaScript:开发人员的比较分析Python vs. JavaScript:开发人员的比较分析May 09, 2025 am 12:22 AM

Python和JavaScript的主要区别在于类型系统和应用场景。1.Python使用动态类型,适合科学计算和数据分析。2.JavaScript采用弱类型,广泛用于前端和全栈开发。两者在异步编程和性能优化上各有优势,选择时应根据项目需求决定。

Python vs. JavaScript:选择合适的工具Python vs. JavaScript:选择合适的工具May 08, 2025 am 12:10 AM

选择Python还是JavaScript取决于项目类型:1)数据科学和自动化任务选择Python;2)前端和全栈开发选择JavaScript。Python因其在数据处理和自动化方面的强大库而备受青睐,而JavaScript则因其在网页交互和全栈开发中的优势而不可或缺。

Python和JavaScript:了解每个的优势Python和JavaScript:了解每个的优势May 06, 2025 am 12:15 AM

Python和JavaScript各有优势,选择取决于项目需求和个人偏好。1.Python易学,语法简洁,适用于数据科学和后端开发,但执行速度较慢。2.JavaScript在前端开发中无处不在,异步编程能力强,Node.js使其适用于全栈开发,但语法可能复杂且易出错。

JavaScript的核心:它是在C还是C上构建的?JavaScript的核心:它是在C还是C上构建的?May 05, 2025 am 12:07 AM

javascriptisnotbuiltoncorc; saninterpretedlanguagethatrunsonenginesoftenwritteninc.1)javascriptwasdesignedAsalightweight,解释edganguageforwebbrowsers.2)Enginesevolvedfromsimpleterterterpretpreterterterpretertestojitcompilerers,典型地提示。

JavaScript应用程序:从前端到后端JavaScript应用程序:从前端到后端May 04, 2025 am 12:12 AM

JavaScript可用于前端和后端开发。前端通过DOM操作增强用户体验,后端通过Node.js处理服务器任务。1.前端示例:改变网页文本内容。2.后端示例:创建Node.js服务器。

Python vs. JavaScript:您应该学到哪种语言?Python vs. JavaScript:您应该学到哪种语言?May 03, 2025 am 12:10 AM

选择Python还是JavaScript应基于职业发展、学习曲线和生态系统:1)职业发展:Python适合数据科学和后端开发,JavaScript适合前端和全栈开发。2)学习曲线:Python语法简洁,适合初学者;JavaScript语法灵活。3)生态系统:Python有丰富的科学计算库,JavaScript有强大的前端框架。

JavaScript框架:为现代网络开发提供动力JavaScript框架:为现代网络开发提供动力May 02, 2025 am 12:04 AM

JavaScript框架的强大之处在于简化开发、提升用户体验和应用性能。选择框架时应考虑:1.项目规模和复杂度,2.团队经验,3.生态系统和社区支持。

JavaScript,C和浏览器之间的关系JavaScript,C和浏览器之间的关系May 01, 2025 am 12:06 AM

引言我知道你可能会觉得奇怪,JavaScript、C 和浏览器之间到底有什么关系?它们之间看似毫无关联,但实际上,它们在现代网络开发中扮演着非常重要的角色。今天我们就来深入探讨一下这三者之间的紧密联系。通过这篇文章,你将了解到JavaScript如何在浏览器中运行,C 在浏览器引擎中的作用,以及它们如何共同推动网页的渲染和交互。JavaScript与浏览器的关系我们都知道,JavaScript是前端开发的核心语言,它直接在浏览器中运行,让网页变得生动有趣。你是否曾经想过,为什么JavaScr

See all articles

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

热工具

VSCode Windows 64位 下载

VSCode Windows 64位 下载

微软推出的免费、功能强大的一款IDE编辑器

WebStorm Mac版

WebStorm Mac版

好用的JavaScript开发工具

mPDF

mPDF

mPDF是一个PHP库,可以从UTF-8编码的HTML生成PDF文件。原作者Ian Back编写mPDF以从他的网站上“即时”输出PDF文件,并处理不同的语言。与原始脚本如HTML2FPDF相比,它的速度较慢,并且在使用Unicode字体时生成的文件较大,但支持CSS样式等,并进行了大量增强。支持几乎所有语言,包括RTL(阿拉伯语和希伯来语)和CJK(中日韩)。支持嵌套的块级元素(如P、DIV),

适用于 Eclipse 的 SAP NetWeaver 服务器适配器

适用于 Eclipse 的 SAP NetWeaver 服务器适配器

将Eclipse与SAP NetWeaver应用服务器集成。

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器