React 组件渲染性能探索

原文地址:Component Rendering Performance in Reactjavascript

文章同步于Github Pines-Cheng/blogcss

React 由于性能好而被广为周知,但这并不意味着咱们可以把这个看成是理所固然。让你的React应用更快的关键Tips之一就是优化你的 render 函数.html

我曾经建立过一个简单的测试,来比较下面不一样条件下的 render() 函数的速度:java

  • 无状态(函数)组件(stateless components) vs 有状态(基于class的)组件components
  • 纯组件(Pure component)渲染 vs 无状态组件
  • React 0.14 vs React 15 在development模式和production模式下的渲染性能

关键结论(TL;DR)

对于那些不耐烦,只是想阅读结果的人来讲,这是如下测试中得出的最重要的结论:react

  • 无状态组件Stateless (functional) components 并不比有状态组件 stateful (class) components
  • React 15 的渲染性能大概比 0.14 快 25%
  • 纯组件 Pure components 更快,由于使用shouldComponentUpdate
  • development 模式下的渲染性能比 production 模式下慢 2–8x倍
  • React 15 development 模式下的渲染性能比 0.14 慢 2x 倍

很惊讶?webpack

与每一个基准同样,弄懂结果的关键是先理解方法。让咱们花一些时间来解释我在这里作了什么,为何要这么作。git

怎样进行性能测试

目标是建立一个很是简单的测试来迭代 render() 函数。我建立了包含三种组件之一的父组件:github

  • 一个无状态组件
  • 一个有状态组件
  • 一个纯粹的有状态组件,即 shouldComponentUpdate 返回 false
// Stateful component
class Stateful extends Component {
  render () {
    return <div>Hello Cmp1: stateful</div>;
  }
}
// Pure stateful with disabled updates
class Pure extends Component {
  shouldComponentUpdate() {
    return false;
  }
  render () {
    return <div>Hello Cmp2: stateful, pure, no updates</div>;
  }
}
// Stateless component
function Stateless() {
  return <div>Hello Cmp3: stateless</div>;
}

测试的组件很简单,并无改变DOM。web

顶层的组件将三种类型的组件每种循环渲染100,000次。同时使用浏览器自带的 Performance.now功能,统计从初始渲染到最后渲染的时间来衡量渲染时间。遗憾的是,咱们不能使用React官方提供的的Perf函数来统计,由于它们不能用在生产环境中。算法

虽然始终传递props以确保更新,但目标组件保持渲染的数据相同。这样咱们能够保证与纯组件的一致性。 DOM不会从这些组件中更新,从而保证其余API(例如布局呈现引擎)的干扰达到最小。

全部的JavaScript代码都运行在纯ES6中,没有转换步骤(没有转换为ES5)

测试运行的浏览器为Chrome 51 (包含经常使用的插件,如一系列的 dev tools, 1Password, Pocket等), 一个纯粹的Chrome Canary 54 (没有插件,没有历史记录,彻底全新的) 和 Firefox 46. OS X 10.11,主机环境为一台2.6 GHz Intel Core i7 处理器的MBP.全部呈现的数据都是取全部浏览器运行结果的平均值。

TJ Holowaychuk’s 的观点中,值得一提的是不安装任何插件的浏览器可以产生一个明显更好的结果。这就是咱们使用一个纯粹的Chrome Canary配置的缘由。不管怎样,咱们普通的用户一般都会在浏览器安装几个插件,咱们并不知道这个会致使多大的性能损失。

执行这些类型的基准测试时的精确度并不容易实现。有时测试会运行速度更慢或更快,致使结果产生误差。这就是为何我屡次运行测试,并统计了全部的结果。您单次测试的结果可能会有所不一样。

全部的 源码 都在Github上,欢迎check out. 每一个框架对应一个文件夹,你可以在里面运行常见的 npm i && npm start命令。这些应用运行在不一样的端口,所以他们可以被同时执行。 readme 文件 提供了更详尽的说明。

咱们已经有了这些前提,如今,让咱们来讨论一下这些发现。

神话:无状态组件更快?

根据React 0.14和React 15,无状态(functional)组件与常规的基于类的状态组件同样快。

image

您会注意到,React 0.14中的测试显示无状态组件与状态组件性能有5%的差别,但我认为这是统计偏差。

可是,当整个生命周期被剥离,且没有引用,没有状态管理,无状态组件为什么不会更快?

根据 Dan Abramov的说法,无状态组件内部封装在一个类中,当前并无进行任何的优化,

@ggrgur There is no “optimized” support for them yet because stateless component is wrapped in a class internally. It's same code path.

React团队已经承诺将对于无状态组件的优化,我确信在React的将来版本之一中将实现。

React中最简单的性能技巧

抛开复杂构建优化问题,React应用程序性能调优的关键在于知道何时渲染,何时不渲染。

这里有一个例子:假设您正在建立一个下拉菜单,并但愿经过设置状态来展开它expanded:true。这种状况下,你必需要渲染整个块,包括下拉列表中的全部列表项才能更改css值吗?千万不要这么干!

这种状况下,shouldComponentUpdate 是你最好的朋友, 赶忙用上它。若是你可以使用shouldComponentUpdate进行优化,就不要使用无状态组件。您可使用Shallow Compare函数来简化流程,但若是这成为React组件核心功能的一部分,我也不会感到惊讶。

更新:从React 15.3开始,React.PureComponent是一个新的基类,能够替代使用Shallow Compare插件或Pure Render mixin的场景。

image

最初的基准测试是在没有添加任何逻辑到render函数的状况下,进行了渲染性能的比较。而一旦添加了计算,Pure Components的优点就会更加明显。咱们能够将其视为一种缓存的方法。
我这么直接缘由之一是看到开发人员不关心改善渲染,由于虚拟DOM将在这方面作了一些处理。可是显然,在涉及到虚拟DOM的diff算法以前,应用的速度还有大量的提高空间。

我不想说你应该在全部的地方使用shouldComponentUpdate。添加大量的逻辑或在组件不多输出相同的HTML代码的地方使用它,就会增长没必要要的复杂度。你应该知道这种生命周期方法的力量,而且合理的使用它。

阻止没必要要的渲染只是其中一方面。上述结果告诉咱们如何提升渲染性能?

优雅的Rendering

下图显示了render()函数对应用程序性能的影响。渲染是复杂操做的开始,最终致使优化的DOM更新。

image

render越简单,更新越快:

  • 执行的JavaScript代码越少越好  — 特别是在移动网站/应用程序
  • 返回更少的更改将有助于加快 virtual DOM 的计算
  • 父组件 (container) 的render极可能会出发全部子孙组件的render(), 这意味着更多的计算。

image

如下是优化render阶段的几个提示:

  • 若是可能,跳过render
  • 在render函数外面,使用变量缓存开销大的计算
  • … 或者将逻辑拆分进多个组件,而后有选择地管理render方法
  • 尽量的返回简单一点
  • 保持render方法的纯粹 (使用函数式编程的思想)
  • 保证state的扁平化,使用浅比较

正如您的应用程序可能在render阶段包含业务逻辑计算同样,React还添加了helpers来加强您的开发体验。让咱们看看它们是如何影响性能的。

使用Production模式进行构建

React代码默认为development模式。使人惊讶的是,development环境下的渲染显着较慢。

image

在构建应用程序时将其指定为production模式的方法是定义环境变量NODE_ENV = production。固然,你只会在生产环境下用到这个,由于development 模式将提供更好的调试体验。

如下是您在Webpack配置中自动执行此变量的方法:

module.exports = {
  plugins: [
    new webpack.DefinePlugin({
      ‘process.env’: {NODE_ENV: ‘”production”’}
    })
  ],
}

这样作不只可以优化你的render性能,还可以提供更小的体积。

别忘了使用UglifyJS plugin剔除没必要要的代码,下面是一个使用例子:

plugins: [
  new webpack.optimize.UglifyJsPlugin({
    sourceMap: false,
    warnings: false,
  }),
],

咱们看到React 15在development模式下与以前的版本相比要慢得多。他们在生产中的比较结果又如何呢?

React 15 更快

React 15 重要更新 是框架与DOM交互的核心变化。 innerHTML 被替换成 document.createElement,现代浏览器中更快捷的选择。
再也不支持Internet Explorer 8可能意味着一些内部流程更加优化。

React 15在渲染过程当中真的更快,但只有在构建模式为production时才能生效。development模式实际上至关慢,主要是由于包含了太多的帮助调试和优化代码的函数。
值得注意的是,这些发现是基于React 15与React-DOM 15的。React Native的development模式下结果可能会有显着差别。也许你能够运行相似的测试,并与社区分享结果。

写在最后

性能开始于在React应用程序中优化render block。该基准测试比较了建立和优化组件的三种方法。你得对他们的使用场景和优缺点了然于心。

可能还有许多其余的基准测试方法,所以绝对不要把你在这里学到的一切看成理所固然。若是您有其余想法欢迎贡献。

Grgur是Modus Create的一名软件架构师,专门从事JavaScript性能,React JS和Sencha框架。若是这篇文章有帮助,也许他16年的从财富100强到政府和初创公司的经验能够帮助你的项目。

相关文章
相关标签/搜索