译者:miaoYu
原文链接
你听说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
所花费的时间 - 它不是免费使用的。最后得出性能的提升比起维护成本是否是值得的。
很简单对吧?不要相信我的话哈,我们用一个例子来演示一下。通过订阅我的消息可以免费获取一个高性能的react
组件。
Get the PDF cheatsheet!
PDF组件
<input type=""text"" name=""first_name"" placeholder=""First name"" id=""ck_firstNameField"" required="""">
<input type=""email"" name=""email"" placeholder=""Email address"" id=""ck_emailField"" required="""">
<button id=""ck_subscribe_button"">订阅我
练习
我把演示例子放到了 fiddle上. 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
部署问题;它解决了你所有问题。
怎样构造好的状态呢?我将在这个帖子上回答 - Luddite’s guide to Redux State(快完成了),不想错过的话?订阅我消息!
最后,我想看到你的问题、建议和意见,如果你有什么想说的,发邮件给我james@jamesknelson.com,我期待你的来信。
扩展阅读
-
Learn Raw React — no JSX, Flux, ES6, Webpack…
-
Object.assign
at MDN
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!