React进阶—性能优化

React性能优化思路

软件的性能优化思路就像生活中去看病,大致是这样的:

  1. 使用工具来分析性能瓶颈(找病根)

  2. 尝试使用优化技巧解决这些问题(服药)

  3. 使用工具测试性能是否确实有提升(疗效确认)

React性能优化的特殊性

看过《高性能JavaScript》这本书的小伙伴都知道,JavaScipt的语言特性、数据结构和算法、浏览器机理、网络传输等都可能导致性能问题。同样是web实现,跟传统的技术(如原生js、jQuery)相比, react的性能优化有什么不同呢?

使用jQuery时,要考虑怎么使用选择器来提高元素查找效率、不要在循环体内进行DOM操作、使用事件委托呀等等。到了React这里,这些东西好像都用不上了。是的,因为React有一个很大的不同点,它实现了虚拟DOM,并且接管了DOM的操作。你不能直接去操作DOM来改变UI,你只能通过改变数据源(props和state)来驱动UI的变化。

说起React的性能分析,还得从它的生命周期和渲染机制说起:

React组件生命周期

图片描述

当 props 和 state 发生变化时,React会根据shouldComponentUpdate方法来决定是否重新渲染整个组件。

React组件树渲染机制

图片描述

父亲组件的props 和 state发生变化时,它和它的子组件、孙子组件等所有后代组件都会重新渲染。


综上所述,可以得出React的性能优化就是围绕shouldComponentUpdate方法(SCU)来进行的,无外乎两点:

  1. 缩短SCU方法的执行时间(或者不执行)。

  2. 没必要的渲染,SCU应该返回false。

React 性能分析工具

Web通用工具:Chrome DevTools

最常用到的是Chrome DevTools的Timeline和Profiles。

  • Timeline工具栏提供了对于在装载你的Web应用的过程中,时间花费情况的概览,这些应用包括处理DOM事件, 页面布局渲染或者向屏幕绘制元素。

  • 通过Timeline发现是脚本问题时,使用Profiles作进一步分析。Profiles可以提供更加详细的脚本信息。

React特色工具:Perf

Perf 是react官方提供的性能分析工具。Perf最核心的方法莫过于Perf.printWasted(measurements)了,该方法会列出那些没必要的组件渲染。很大程度上,React的性能优化就是干掉这些无谓的渲染。

有童鞋开发了Chrome扩展程序“React Perf”(戳这里)。相比自己在代码中插入Perf方法进行分析,这个小工具更加灵活方便,墙裂推荐!

案例分析:TodoList

TodoList的功能很简单,就是对待办事项进行增加和删除操作:
图片描述

import React, {PropTypes, Component} from 'react';class TodoItem extends Component {static propTypes = {deleteItem: PropTypes.func.isRequired,item: PropTypes.shape({text: PropTypes.string.isRequired,id: PropTypes.number.isRequired,}).isRequired,};deleteItem = ()=>{let id = this.props.item.id;this.props.deleteItem(id);};render() {return (<div><button style={{width: 30}} onClick={this.deleteItem}>X</button>&nbsp;<span>{this.props.item.text}</span></div>);}}class Todos extends Component {// 构造constructor(props) {super(props);// 初始状态this.state = {items: this.props.initialItems,text: '',};}static propTypes = {initialItems: PropTypes.arrayOf(PropTypes.shape({text: PropTypes.string.isRequired,id: PropTypes.number.isRequired,}).isRequired).isRequired,};addTask = (e)=> {e.preventDefault();this.setState({items: [{id: ID++, text: this.state.text}].concat(this.state.items),text: '',});};deleteItem = (itemId)=> {this.setState({items: this.state.items.filter((item) => item.id !== itemId),});};render() {return (<div><h1>待办事项</h1><form onSubmit={this.addTask}><input value={this.state.text} onChange={(v)=>{this.setState({text:v.target.value});}}/><button>添加</button></form>{this.state.items.map((item) => {return (<TodoItem key={item.id}item={item}deleteItem={this.deleteItem}/>);})}</div>);}
}let ID = 0;
const items = [];
for (let i = 0; i < 1000; i++) {items.push({id: ID++, text: '事项' + i});
}class TodoList extends Component {render() {return (<Todos initialItems={items}/>);}
}export default TodoList;

在待办事项输入框里输入一个字母,接下来我们以这个行为为例来进行性能分析和优化。

第一次优化

使用Chrome开发者工具的Timeline记录下这个过程:
clipboard.png

重点关注出现的红色块,代表这个行为存在性能问题。从上图我们可以看出,耗时的Event(keypress)长条花了98.8ms,其中98.5ms用于脚本处理,可见脚本问题是罪魁祸首。

接着,我们使用Profiles来进一步分析脚本问题:
clipboard.png

对Total Time进行降序排列,发现耗时最长的是dispatchEvent,来自react源码。这时,我们就可以确定是react这一层出现了性能问题。

嗯,轮到Perf出场了:
clipboard.png

上图表示,有1000次不必要的渲染发生在TodoItem组件上.

打开react面板,我们来看看组件的层次和相应的state、props值:
clipboard.png

TodoItem是Todos的子组件,当我们在输入框输入字母“s”时,Todos的state值发生改变时,文章开头所说的react的渲染机制导致Todos下的1000个TodoItem组件都会重新渲染一次。但是,TodoItem的展现其实没有任何变化。
从代码中,我们可以看出,TodoItem组件展现只跟props(deleteItem、item)相关。props没有变化,TodoItem就没必要渲染。

所以,我们应该优化下TodoItem的SCU方法:

class TodoItem extends Component {...//在props没有变化的时候返回false,不重新渲染shouldComponentUpdate(nextState,nextProps) {if(this.props.item == nextProps.item && this.props.deleteItem == nextProps.deleteItem){return false;}return true;}render() {... }}

(PS: TodoItem中的SCU方法,使用的是浅比较,也可以使用PureComponent代替。实际项目中,往往需要使用复杂的深比较,可以考虑使用Immutable.js)

验证下优化效果,使用Perf测试,发现1000个多余的渲染被干掉了!
再次使用Timeline分析,Event(keypress)耗时从98.5ms降到了26.49ms,性能提升了2.7倍:
clipboard.png

疗效还不错!

第二次优化

通过SCU返回false,我们避免了无谓的渲染。但是,我们还是调用了1000次TodoItem的SCU方法,这也是一笔不小的性能开支。

是否可以不用调用呢?通过合理地规划组件粒度,可以做到:

//将增加待办事项抽象成一个组件
class AddItem extends Component{constructor(props) {super(props);this.state = {text:""};}static PropTypes = {addTask:PropTypes.func.isRequired};addTask = (e)=>{e.preventDefault();this.props.addTask(this.state.text);};render(){return (<form onSubmit={this.addTask}><input value={this.state.text} onChange={(v)=>{this.setState({text:v.target.value});}}/><button>添加</button></form>);}
}class Todos extends Component{constructor(props) {super(props);this.state = {items: this.props.initialItems,};}static propTypes = {initialItems: PropTypes.arrayOf(PropTypes.shape({text: PropTypes.string.isRequired,id: PropTypes.number.isRequired,}).isRequired).isRequired,};addTask = (text)=>{this.setState({items: [{id: ID++, text:text}].concat(this.state.items),text: '',});};deleteItem = (itemId)=>{this.setState({items: this.state.items.filter((item) => item.id !== itemId),});};render() {return (<div><h1>待办事项V3</h1><AddItem addTask={this.addTask}/>{this.state.items.map((item) => {return (<TodoItem key={item.id}item={item}deleteItem={this.deleteItem}/>);})}</div>);}
}

把增加待办事项抽象成一个AddItem组件。这样一来,组件树从原来的
clipboard.png

变成

clipboard.png

输入信息时触发变化的text这个state值,被下放到AddItem组件来管理,因此不会导致兄弟组件(TodoItem)的重新渲染。

再次运行Timeline测试,这时Event(keypress)耗时从26.49ms降到了7.98ms,性能提升了2.3倍:
clipboard.png

至此,性能优化完毕~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/259280.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

内蒙古银行银行招聘计算机研究生,内蒙古银行招聘公告

出国留学网考研报名资讯&#xff1a;内蒙古2015考研报考公告&#xff0c;希望仔细阅读考研报名公告&#xff0c;及时进行报名&#xff0c;尽量避开报名高峰期!内蒙古2015考研报考公告一、关于报考点的的安排我区共设12个报考点&#xff1a;呼和浩特市招生考试管理中心、内蒙古大…

ubuntu 13.04 telnet 详细配置

1. sudo vi /etc/xinetd.d/telnet并加入以下内容&#xff1a;# default: on# description: The telnet server serves telnet sessions; it uses \# unencrypted username/password pairs for authentication.service telnet{disable noflags REUSEsocket_type streamwait …

C++定义隐式转换函数,将类转换为内部的一个成员变量

C中单参数构造函数若不声明为explict&#xff0c;在合适的场合可以产生隐式转换&#xff1a;由成员变量类型转换为类类型。 下面的代码展示如何实现反向的转换&#xff1a; Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/…

2015年百度面经

百度问的是开放性的问题&#xff0c;应该是为了考察你的综合能力吧&#xff0c;问了两个问题 一&#xff0c;html&css 涉及的内容 块元素与行内元素&#xff0c;浮动&#xff0c;清除浮动 1&#xff0c;一个100px的容器&#xff0c;里面塞了一个空的div&#xff0c;这个di…

计算机网页设计与制作论文,网页设计与制作论文

二十一世纪是信息化的时代&#xff0c;通过互联网&#xff0c;就能达到足不出户便可了解世界的目的。为了加深对互联网的了解&#xff0c;《网页设计与制作》这门课的出现就成为了必然。1《网页设计与制作》现状问题分析(1)对课程不了解很多学生都有这个困惑&#xff0c;这门课…

mybatis中#{}和${}的区别

http://www.cnblogs.com/davidwang456/p/4929426.html转载于:https://www.cnblogs.com/xtdxs/p/6666017.html

游标定位:Cursor类

关于 CursorCursor 是每行的集合。使用 moveToFirst() 定位第一行。你必须知道每一列的名称。你必须知道每一列的数据类型。Cursor 是一个随机的数据源。所有的数据都是通过下标取得。关于 Cursor 的重要方法&#xff1a;close() 关闭游标&#xff0c;释放资源copyStringToBuf…

Supervised Descent Method and its Applications to Face Alignment

广播说明&#xff1a; 进入深度学习时代&#xff0c;如下的方法已经失去可比性&#xff0c;且我们的代码实现地很粗糙&#xff0c;如果坚持要用&#xff0c;推荐如下代码 https://github.com/wanglin193/SupervisedDescentMethod &#xff08;看起来作者对sdm实现的不错&…

导出Excel神器最终版

泛型列表导出Excel&#xff1a; 最近好多导出问题就整这么个玩意共享给大家public class Export{/// <summary>/// 泛型导出Excel/// </summary>/// <param name"strCaption">Excel文件中的标题</param>/// <param name"pList"…

国外计算机课程lab,计算机系统实验之bomblab

今天刚刚验收CSAPP实验3&#xff0c;趁着余温&#xff0c;记录一下这个实验&#xff0c;顺便回顾下CSAPP课程的相关知识。实验目的1.使用gdb工具反汇编出汇编代码&#xff0c;结合c语言文件找到每个关卡的入口函数。然后分析汇编代码&#xff0c;分析得到每一关的通关密码。2.熟…

批量实现ssh免交互认证

因为要部署一批服务器&#xff0c;为了以后管理方便&#xff0c;要进行免密认证。一台一台做很费时&#xff0c;脚本又得手动输密码。于是上网搜了搜&#xff0c;发现一个非常简单的免交互认证&#xff0c;不需要入密码即可完成&#xff01;环境&#xff1a;centos 6.8 虚拟机V…

CSS兼容IE6,IE7,FF的技巧(COPY来的,还没看)

一、CSS HACK 以下两种方法几乎能解决现今所有HACK.翻阅很多资料&#xff0c;已测试可以使用。 1, !important 随着IE7对!important的支持, !important 方法现在只针对IE6的HACK.(注意写法.记得该声明位置需要提前.) PLAIN TEXT CSS: #wrapper { width: 100px!important; /* IE…

计算机复制粘贴教案,信息技术《文本的复制与移动》教案

一、教学内容分析本课是小学信息技术教材四年级下册第十八课文本的复制与移动。是在学生掌握了文件夹的复制、移动&#xff0c;以及掌握了Word的启动、退出&#xff0c;在Word中输入文字并保存等内容之后的又一个知识点&#xff0c;学好这一课为学生以后学习文本的编辑与操作&a…

ajax基础知识

AJAX 指异步JavaScript及XML&#xff08;Asynchronous JavaScript And XML&#xff09;运用ajax步骤&#xff1a;创建对象&#xff08;注意IE6兼容问题&#xff09;、连接服务器、发送请求、接收返回ajax的readystate属性&#xff1a;0&#xff1a;表示未初始化1&#xff1a;表…

SDM For Face Alignment 流程介绍及Matlab代码实现之预处理篇

SDM全称为 Supervised Descent Method&#xff0c;是一种机器学习的方法&#xff0c;可以被用来做Face Alignment. 下面我们将通过matlab代码来梳理整个实现的过程。 预处理阶段 Input&#xff1a; ../data/lfpw/trainset &#xff08;811张图片&#xff09; Output: mean_…

Nginx初识

configure脚本&#xff1a;1、cat <EOF >>test.txtEOF多行追加2、uname用于打印当前操作系统的相关信息3、2>/dev/null/dev/null 代表空设备文件 1 表示stdout标准输出&#xff0c;系统默认值是1&#xff0c;所以">/dev/null"等同于"1>/dev/n…

linux中shell变量$#,$@,$0,$1,$2的含义解释

linux中shell变量$#,$,$0,$1,$2的含义解释: 变量说明: $$ Shell本身的PID&#xff08;ProcessID&#xff09; $! Shell最后运行的后台Process的PID $? 最后运行的命令的结束代码&#xff08;返回值&#xff09; $- 使用Set命令设定的Flag一览 $* 所有参数列表。如"$*&quo…

分享25个新鲜出炉的 Photoshop 高级教程

网络上众多优秀的 Photoshop 实例教程是提高 Photoshop 技能的最佳学习途径。今天&#xff0c;我向大家分享25个新鲜出炉的 Photoshop 高级教程&#xff0c;提高你的设计技巧&#xff0c;制作时尚的图片效果。这些教程可以帮助把你的想法变成现实&#xff0c;并创造新的东西。 …

北京林业大学计算机技术复试,北京林业大学计算机应用技术04年考研复试办法...

一、复试小组的组成原则和人数计算机应用技术学科硕士研究生复试小组由具有副教授以上(含副教授)职称的研究生导师和具有博士学位的副教授组成&#xff0c;人数为3~5人。组长有学科负责人担任&#xff0c;设秘书1名。二、专业课的考核形式和要求专业课的考核形式为笔试和口试。…

SDM For Face Alignment 流程介绍及Matlab代码实现之训练篇

SDM 训练阶段的任务如下&#xff1a; 载入标准化的数据&#xff08;包括400*400的正脸及特征点&#xff09;对每一张标准化的图片&#xff0c;模拟人脸检测仪&#xff0c;产生10个扰动的人脸框及相应的初始特征点x0。求解Δx,Φ,其中Δxx∗−x0,x∗表示true shape,Φ表示每个特…