您的位置:首页 > Web前端 > React

【React】什么时候使用shouldComponentUpdate方法?

2017-08-01 14:11 609 查看
你听说React是一个很快的前端框架,决定试一试。然后你找了个样例开始了React之旅,这时你注意到
shouldComponentUpdate
PureRenderMixin
,通过google找到一些技巧让React变得更快。但是React真的快吗?

答案是React确实很快…不过是在某些时候。其他时候,你应该使用shouldComponentUpdate,但是其他时候是什么时候呢?如果只是一个是或否的问题,你可能会问确定是否使用
shouldComponentUpdate
,会那么容易吗?你可能不信,但它就是那么容易。

shouldComponentUpdate
将带来可测量和可感知的提升?

如果不能,那就别用。

那你的意思是我可能不会用它?

你可能应该避免用它。据React团队的说法,
shouldComponentUpdate
是一个保证性能的紧急出口,意思就是你不到万不得已就别用它。当然这也不是绝对。

如果它没用,React也不会有这样一个紧急的性能出口。当你使用它时,问自己是否需要?总之,你可能偶尔还是要用到的。

我们来看看背后的原理,从这里开始吧:

添加
shouldComponentUpdate
方法将拖慢你组件的性能。

是的,你没看错,一个保证性能的出口的确能拖慢组件的性能。如何做到的呢?回答这个问题,我们需要先问什么是React?

React非常聪明的实现了
shouldComponentUpdate


但是,React做的不仅仅是算出是否该更新组件。还需要确定如何去更新它。

React是如何确定什么时候去渲染组件的呢?

这是一个好问题!所以你了解你为每个组件写的渲染方法吗?它看起像返回了一段类似Html的jsx代码(或者你用Raw React会返回ReactElement对象),但是你会惊讶的听到渲染方法返回的是一个普通的js对象。看起来是这样的:

{
type:  'ul',
props: {
className:  'what-do-you-want-to-do-tonight'
},
children:  [
{ type:  'li', children:  'The same thing we do every night, pinky.'  },
]
}


当然,它可能会被装饰成
JSX
或者 冒充成
React.createElement
,但是正确形式的
render()
的输出是一个你标记的对象。如果那个对象在预渲染中发现没有改变,那么它将不会被渲染到DOM上。

或者用其他方式渲染,如果我们伪装
render
方法接受
props
来取代直接触发
render
方法,那么代码看起来是这样的:

shouldComponentUpdate(nextProps)  {
return  !deepEquals(render(this.props), render(nextProps))
}


我们知道
deepEquals
对于简单对象处理是很快的,但是对于嵌套很深的对象处理很慢,所以这个近似值给我们一个很好的法则:

如果
render
方法返回的对象很小,但是
props
对象很大,那么
shouldComponentUpdate
将对性能带来很大的拖累。

但是你会说有种情况是
render
方法返回的值足够大以至于你可以通过
shouldComponentUpdate
获得某些东西?

使用
shouldComponentUpdate
的收获通常是无关紧要的

所以当你渲染一个很大的table,你从props中提取数据,调用render方法来计算,然后通过Immutable.js 来判断数据是否相等。

在上面的场景中,
shouldComponentUpdate
比起React默认的处理方式带来了巨大的性能提升。事实上,以我的经验来看,至少10倍。但是在这个场景中,如何收获微不足道?答案是肯定的。

小于1毫秒的10倍仍然小于1毫秒。

Donald Knuth 曾经写过:“过早的优化是一切的万恶之源”。

维护shouldComponentUpdate很困难

所以如果我不得不猜测,React团队把
shouldComponentUpdate
叫做紧急出口,而不是叫性能按钮的原因就是维护困难,但是老实说,我认为紧急出口这个叫法不合适。
shouldComponentUpdate
应该更像是无保护的性行为。

原因是这样的,
shouldComponentUpdate
有时候是必要的,它能让你的应用性能更好。但是它也能引起巨大的bugs,而且不容易被发现。

维护
shouldComponentUpdate
的代码很难。比如加一个新的prop到render方法,但是忘了更新
shouldComponentUpdate
?这就是一个bug。最糟糕的是这些bugs在测试中不会出现,有可能是你第一次给客户展示的时候出现。

但是如果
shouldComponentUpdate
真的容易引起很多bug,它显然需要一些使用定律,所以问题就变成了…

什么时候使用
shouldComponentUpdate

一旦
shouldComponentUpdate
给你带来了可感知的性能提升,就使用它。

希望上一节已经说服你,
shouldcomponentupdate
有一些缺陷。带着这个想法,当你确定要使用它时,你需要一把尺子,是一个非常花哨的技术。

测量性能提升

这里有个 方法 你可以测量
shouldComponentUpdate
带来的提升, 下面我将集中讨论JavaScript分析器.

什么是分析器?它就是我之前提到的尺子。一个好的分析器可以让你测量你所有的程序。它可以得到你每个方法运行的时间。



通过比较render方法在添加
shouldComponentUpdate
之前和之后花费的时间,我们能得到性能提升的数值。当然,我们也将需要考虑
shouldComponentUpdate
所花费的时间 - 它不是免费使用的。最后得出性能的提升比起维护成本是否是值得的。

练习

我把演示例子放到了 fiddle(https://jsfiddle.net/nsh4b3Ly/2/)上. Enterprise组件为了满足需求,将使用
shouldComponentUpdate
; 然后对原始数据进行处理, 使得它频繁的触发渲染方法, 其次是它的 props 包含不可变数据.

你只需要进入 the project按步骤来完成练习.

开始之前,你需要知道如何打开浏览器的分析器。你可以在这里了解到:Chrome 或者 Firefox

第一步: 测试不加shouldComponentUpdate的版本

当触发点击的时候,我们来测量渲染的时间:

打开js分析器



开始记录



点击 “Toggle synergy!” 让页面循环5秒



点击你开始记录的按钮来停止记录

在列表的最上面部分找到render方法,记录所花的时间,



如果render所花的时间足够的少,没有出现在时间花费列表前面,恭喜你!你不用使用
shouldComponentUpdate
。或者说你在解决其他性能问题前你不必考虑它。

第二步: 测试加了shouldComponentUpdate的版本

这一步,我们加了
shouldComponentUpdate
到App组件,来测试渲染时间。
shouldComponentUpdate
方法,代码已经为你准备好了:

shouldComponentUpdate(nextProps, nextState)  {
return  !Immutable.is(this.state.synergy, nextState.synergy)
},


当你加了
shouldComponentUpdate
,重复第一步的步骤,以下是结果:

组件中写了
shouldComponentUpdate
的render:



shouldComponentUpdate
:



上面的方法不一定准确,实际的结果可能相差很大。如果你不相信我,你可以尝试上面的步骤来试试。如果结果不准确,那么怎么抉择呢?

讲真的,这种方法得出的结果相差很大,这种方法只可以给你提供一个粗略的测算。(请不要把这个方法给统计学家看)

抉择

从我的经验来看,如果添加
shouldComponentUpdate
方法,可以节约一半的渲染时间,那么它是对你有利的。

如果没加
shouldComponentUpdate
之前的渲染只花了100ms,那就没有必要用
shouldComponentUpdate
了。

正确的写
shouldComponentUpdate
方法

现在我摇着你的手告诉你用“Immutable.js”就可以了,此时我猜你的内心os应该这样的:



Immutable.js不是完美的解决方案,也不是唯一的,ES6的Object.assign也可以达到差不多的效果

如果不可变的状态不能解决你的问题,那什么才可以呢?

我很高兴你这么问!正真的解决方案是良好的状态结构。事实上,好的状态结构将解决你
shouldComponentUpdate
部署问题;它解决了你所有问题。

原文链接:http://jamesknelson.com/should-i-use-shouldcomponentupdate/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  react