谈反应性能的优化方法
反应性能的优化
软件性能优化的思路就像是在生活中去看医生,大致是这样的:
使用工具分析性能瓶颈(找到根本原因)
尝试使用优化技术来解决这些问题(药物治疗)
使用工具测试性能是否真的提高(功效确认)
我遇到react只是为了尽快完成项目,但是在后面的代码评审中发现有很多地方需要优化,所以做了总结。
代码拆分应该避免重复呈现。使用不可变的数据结构组件来尽可能地拆分和分离列表类组件。优化绑定功能。不要滥用Props ReachComponentServer进行服务器端呈现组件代码拆分
代码拆分可以帮助你“懒加载”代码。如果不能直接减少应用程序量,可以尝试将应用程序从单个捆绑包拆分成具有多个动态代码的单个捆绑包。
Webpack提供了三种代码分离方法,详见webpack官网
入口起点:使用入口配置手动分离代码。为了防止重复:使用SplitChunks来复制和分离块。动态导入:通过模块的内联函数调用分离代码。在这里,我们主要了解第三种动态导入方法。
1、例如,可以把下面的导入方式
从“”导入{ add }。/math ';console.log(add(16,26));改写成动态导入的形式,使得数学模块在第一次加载时不加载,从而减少了第一次加载的资源量。
导入('。/math’)。然后(math={ console.log(math.add(16,26));});2.例如,参考高阶组件react-loaded进行动态导入。
从“react-loaded”导入可加载的;从“”导入加载。/loading-component ';const LoadableComponent=可加载({ loader: ()=import('。/my-component '),loading: Loading,});导出默认类App扩展React。组件{ render(){ return LoadableComponent/;}}上述代码第一次加载时,会先显示一个加载组件,然后动态加载my-component的代码。加载组件代码后,它将替换加载组件。
应组件更新避免重复渲染
当组件的道具或状态发生变化时,React通过将新返回的元素与先前呈现的元素进行比较来确定是否有必要更新实际的DOM。当它们不相等时,React会更新DOM。
在某些情况下,您的组件可以通过重写这个生命周期函数shouldComponentUpdate来加快速度,该函数在重新呈现过程开始之前被触发。默认情况下,此函数返回true,这使React能够执行更新。
为了进一步说明问题,请参考官方网站的图表进行解释,如下所示(SCU表示shouldComponentUpdate,绿色表示return true(需要更新),红色表示return false(不需要更新);VDOMEq表示虚拟DOM比较,绿色表示一致性(不需要更新),红色表示变化(需要更新)):
根据渲染过程,首先确定是否需要更新shoulcomponentupdate(SCU)。如果需要更新,调用组件的render生成一个新的虚拟DOM,然后与旧的虚拟DOM (vDOMEq)进行比较。如果比较一致,则不会更新。如果比较不同,DOM将根据最小粒度变化进行更新。如果SCU不需要更新,则直接保持不变,其子元素保持不变。
C1根节点,绿色的SCU和红色的vDOMEq,表示它需要更新。C2节点,红色SCU,意味着不需要更新,C4和C5作为其子节点不需要检查更新。C3节点,绿色的SCU和红色的vDOMEq,表示需要更新。C6节点,绿色的SCU和红色的vDOMEq,表示需要更新。C7节点,红色SCU,表示不需要更新。C8节点,绿色SCU,表示React需要渲染这个组件;绿色的vDOMEq表示虚拟DOM是一致的,DOM没有更新。因此,我们可以根据自己的业务特点重载shouldComponentUpdate,只有在确认真正的DOM需要更改时才返回true。一般的做法是比较组件的道具和状态是否真的改变,如果改变就返回true,否则返回false。举出官网的案例。
类反按钮扩展了反应。组件{构造器(道具){ super(道具);this . state={ count : 1 };} should component update(nextProps,nextState) { if (this.props.color!==nextprops . color){ return true;} if (this.state.count!==nextState.count) {返回true}返回false} render(){ return(button color={ this . props . color } onClick={()=this . setstate(state=({ count : state . count 1 })} count : { this . state . count }/button);}}在上面的代码中,shouldComponentUpdate只检查道具.颜色和状态.计数的变化.如果这些值不变,组件将不会更新。当您的组件变得更加复杂时,您可以使用类似的模式进行“浅层比较”,这用于比较属性和值,以确定是否需要更新组件。这种模式非常常见,所以React提供了一个辅助对象来实现这种逻辑——继承自React。PureComponent
在大多数情况下,您可以使用“反应”。PureComponent,而不是编写自己的shouldComponentUpdate,这只是做了一个粗浅的比较。但是,当比较目标是引用类型数据时,浅层比较会忽略属性或状态的突然变化,因此不能使用,需要注意以下不可更改的数据。
注意:变异数据是指一个变量的引用没有变化(指针地址没有变化),但是引用指向的数据发生了变化(指针指向的数据发生了变化)。例如,const x={foo:'foo'}。X.foo='none '是突变。
使用不可变的数据结构
引用官网的例子说明突变数据的问题。例如,假设您想要一个单词列表组件呈现一个逗号分隔的单词列表,并使用一个带有名为单词添加器的单击按钮的父组件向子列表添加一个单词。以下代码不正确:
类ListOfWords扩展了React。pure component { render(){ return div { this . props . words . join(',')}/div;} }类WordAdder扩展了React。组件{构造器(道具){ super(道具);this . state={ words :[' marklar ']};this . handleclick=this . handleclick . bind(this);} handleClick() {//这一段将导致代码无法按预期运行。const words=this . state . words;words . push(' marklar ');this . setstate({ words : word });} render() { return (div按钮onClick={ this . handleclick }/listfowds words words={ this . state . words }//div);}}代码无法正常工作的原因是PureComponent只对this.props.words的新旧值进行了“浅比较”.即使在handleClick中修改单词值后向数组中添加了一个新单词,this.props.words的新旧值在比较时也是相同的(引用对象比较),因此ListOfWords永远不会被呈现。避免此类问题的最简单方法是避免使用其值可能突然变化的属性或状态,例如:
1.数组使用concat,对象使用Object.assign()
handleClick(){ this . setstate(prev state=({ words : prev state . words . concat([' marklar '])}));}//假设我们有一个名为colormap的对象。以下方法不污染原始对象函数update color map(color map){ return object。assign ({},colormap,{ right : ' blue ' });}2.ES6支持数组或对象的扩展语法
handleClick(){ this . setstate(prev state=({ words :[.prevState.words,' marklar'],}));};函数updateColorMap(colormap){ return }.colormap,right : ' blue ' };}3.使用不可变的. js,它是不可变的数据
不可变的. js使变更跟踪变得方便。每一次更改都会产生一个新的对象,所以我们只需要检查索引对象是否发生了更改。
const SomeRecord=不可变。记录({ foo : null });const x=new some record({ foo : ' bar ' });const y=x.set('foo ',' baz ');x===y;//false在本例中,X突变后会返回一个新的索引,因此我们可以安全地确认X已经被更改。
不变的数据结构有助于我们轻松跟踪对象的变化,从而快速实现shouldComponentUpdate。
具体使用请参考以下文章:React中不变的详细解释和实践
尽可能地分离和分离组件
组件被尽可能地细分,比如一个输入列表组件,它可以将列表划分为一个PureComponent,并且只在列表数据发生变化时更新。否则,当输入值改变并且页面被重新呈现时,列表也需要进行不必要的DOM区分。
列表类组件的优化
键属性提供了组件类之外的另一种组件识别方式。通过密钥识别,当组件被添加、删除、修改、排序等。DOM顺序可以根据键值的位置直接调整,告诉React避免不必要的渲染和性能浪费。例如,对于基于排序的组件渲染:
var items=sort by(this . state . sortinghalgorithm,this . props . items);return items . map(function(item){ return img src={ item . src }/});当顺序改变时,React将改变元素并改变img的src属性。说明这种操作效率很低。此时,我们可以向组件添加一个关键属性来唯一标识组件:
返回img src={item。src}键={item。id }/添加键后,React不是diff,而是直接使用insertBefore操作来移动组件的位置,这是移动DOM节点最高效的方式。
绑定功能
绑定方式:一般有三种方式:
1.构造函数绑定
建造师(道具){ super(道具);this . handleclick=this . handleclick . bind(this);//在构造函数中绑定}//然后可以p onClick={this.handleClick}2。使用时绑定。
p onclick={ this . handleclick . bind(this)} 3。使用箭头功能
Testclick={()={this。handleclick()} }/以上三种方法中,第一种是最好的。
因为第一个构造函数在组件初始化时只执行一次,
第二个组件在每次渲染时执行
第三个在每次渲染时生成一个新箭头函数。示例:测试组件的点击属性是一个箭头函数,测试组件将
因为这个新生成的箭头函数而更新,导致测试组件不必要的呈现。
不要滥用道具
道具尽量只传输需要的数据,避免不必要的更新,避免使用{.道具}
ReactDOMServer执行服务器端呈现组件
为了让用户更快地看到完整的渲染页面,可以采用服务器端渲染技术。这里,让我们来看看ReactDOMServer。
要实现SSR,可以用nodejs框架(Express、哈比神、Koa)启动一个web服务器,然后调用renderToString方法将你的根组件渲染成字符串,最后输出到response。
//使用来自“react-dom/server”的expression import { renderToString };从“”导入我的页面。/MyPage ';app.get('/',(req,res)={ res.write('!DOCTYPE HTMl HTMl head title my Page/title/head body’);RES . write(' div id=' content ' ');RES . write(renderToString(MyPage/));RES . write('/div/body/html ');RES . end();});客户端使用渲染方法生成HTML
从“react-dom”导入ReactDOM从“”导入我的页面。/MyPage ';ReactDOM.render(MyPage /,document . getelementbyid(' app '));React性能测试工具
在react16之前,我们可以使用react-addons-perf工具进行检查,但是在最新的16版本中,我们只需要添加?反应优先.首先,让我们看看react-addons-perf。
React-addons-perf是React官方推出的性能工具包,可以打印出组件渲染的时间、次数和浪费的时间。简单说几个API,具体用法参考官网:
Perf.start()开始录制Perf.stop()结束录制Perf.printInclusive()查看所有设计好的组件渲染Perf.printWasted()查看不必要的浪费组件渲染,然后通过添加找出react16版本的方法?使用react_pref,可以通过查看chrome浏览器性能中的User Timeing来检查组件的加载时间。点击录制开始录制,注意录制时间不能超过20s,否则chrome可能会挂起。
以上就是本文的全部内容。希望对大家的学习有帮助,支持我们。
版权声明:谈反应性能的优化方法是由宝哥软件园云端程序自动收集整理而来。如果本文侵犯了你的权益,请联系本站底部QQ或者邮箱删除。