[译]高性能React: 3个工具提高你app的速度

[译]高性能React: 3个工具提高你app的速度

原文链接:High Performance React: 3 New Tools to Speed Up Your Apps

React其实已经蛮快的了,但是一些使用错误很容易引起性能的问题。组件挂载慢,组件树层级过深以及不必要的循环渲染会让app使用起来感觉很慢.

幸运的是,有不少工具,有些甚至是内置到React的,对诊断这些性能问题很有帮助。本文中,我会对于提高React速度的工具和技术的部分进行高亮显示。每个部分都有一个交互的demo!

Tool #1: The Performance Timeline

React 15.4.0 介绍了一个新的性能timeline特性,你能通过该特性看到组件的整个生命周期,如挂载(mounted),更新(updated),卸载(unmounted)等等。同时也提供了相关组件的生命周期可视化。

Note:这个特性现在只在Chrome,Edge和IE有效,因为尚未在所有浏览器中实现User Timing API.

如何生效

  1. 打开app并在访问的链接后面加参数: react_perf 。如:http://localhost:3000?react_perf
  2. 打开Chrome DevTools的Performance,点击record。
  3. 执行想要分析的操作
  4. 停止录制
  5. 查看Timing


理解输出的内容

每个被着色的条都展示一个组件执行工作的时间。因为JavaScript单线程的特性,所以一个组件无论何时挂载或者渲染,都会独占主线程并阻止其他代码运行。

方括号里面的字,如[update]描述了组件生命周期的哪一部分正在执行。timeline会把每个步骤分解,所以你可以区分出诸如[componentDidMount][componentWillReceiveProps][ctor](constructor) 和[render]的细微的时间差。

堆叠的条表示组件树。在React中,有很深的组件树是很典型的,但是如果优化经常触发挂载的组件,可以帮助减少包装器组件的数量,因为每个组件都有增加一点性能和内存的损耗。

这里需要注意的一点是,timeline中的数值是用于React开发构建的,所以相对于生产会慢许多。事实上,使用timeline也会使你的app变慢。虽然这些数值并不代表真实环境的性能,但是不同组件的相对时间是精确的。而且,组件是否更新并不取决于生产环境。

Demo #1

出于好玩的目的,我把一个TodoMVC的app折腾出了一些性能问题。打开chrome dev tools,打开Performance,点击record,就可以看到timeline了。试着加一些TODOs,然后停止record,然后查看timeline。看看你能否找出那个组件导致了性能问题:)

Tool #2: why-did-you-update

一个影响React性能的通病是一些不必要的循环渲染。React组件默认会在父组件渲染时重绘,即使传递的props值并未改变。举个例子,有个简单的组件如下:

class DumbComponent extends Component {
  render() {
    return <div> {this.props.value} </div>;
  }
}

它的父组件是这样的:

class Parent extends Component {
  render() {
    return <div>
      <DumbComponent value={3} />
    </div>;
  }
}

一旦父组件渲染,DumbComponent 组件就会重绘,即使props未改变。

一般来说,如果render方法执行了,虚拟dom未改变,就会造成循环渲染的浪费,因为render方法应该是单一,无副作用的。在大型的React app里,检测发生了这个问题的根源比较难,幸运的是有个工具可以帮助我们。

Using why-did-you-update

why-did-you-update 是个挂钩到React、用于检测潜在不必要组件渲染的库。它会检测组件render方法的调用,即使props值并为改变。

Setup

  1. 使用npm安装:npm i --save-dev why-did-you-update
  2. 添加下面的代码片段到你的项目中:
import React from 'react'
if (process.env.NODE_ENV !== 'production') {
  const {whyDidYouUpdate} = require('why-did-you-update')
  whyDidYouUpdate(React)
}

Note: 该工具方便与于本地开发,但会使你的应用变慢,所以确保生产环境下被禁用。

理解输出的内容

why-did-you-update 监测你的应用运行及记录不必要改变的组件。这可以让你看到在循环渲染之前和之后的props,表面这是否是不必要的渲染。

Demo #2

为了演示 why-did-you-update,我在TodoMVC应用中安装了这个库。打开控制台,加几项TODOs,查看控制台输出。

查看demo

应用中的一些组件执行了不必要的渲染方法。试着实现上述的技术来防范一些不必要的渲染。如果完成正确,控制台就没有why-did-you-update的输出日志

Tool #3: React Developer Tools

React Developer Tools 的chrome插件已经内置了组件更新可视化的特性。这有利于检测不必要的循环渲染。为了使用这个插件,首先确保已经安装。接下来,在Chrome DevTools中切换至React的选项卡,选中“Highlight Updates”。

接下来,仅需简单的使用一下你的应用。使组件交互,然后看一下DevTools的神奇作用。

理解输出的内容

React Developer Tools会将在同一时间重复渲染的组件高亮显示。根据更新的频率使用不同的颜色。从蓝,绿,黄,红表示频率从低到高

看到黄或者红色不一定是坏事。当调整一个slider或者UI元素频繁触发更新,都是可以预期的。但如果只是点击一个普通的按钮然后看到红色框,可能就是哪里出岔子了。使用这个工具的目的是为了发现不必要更新的插件。作为一个应用开发者,应该有在一定时间内哪些组件应该被更新的意识。

Demo #3

为了演示组件高亮,我在TodoMVC应用里安装了一些不必要的组件。

demo

打开上面的链接,然后打开React Developer Tools,启用更新高亮。当你在输入框输入时,就会看到所有TODOs不必要的高亮。当你输入越快时,会看到颜色指示变化越频繁。

修复不必要的渲染

一旦定位到了应用中的不必要重绘的组件,可以使用以下几种修复方法。

使用纯组件

在上面的例子中,DumbComponent是一个它props值的纯函数。也就是说,这个组件仅在它props值改变的时候才需要重绘。React有一个特殊类型的内置组件叫纯组件,说的就是这种使用案例。

从React.PureComponent继承而不是从React.Component.

 class DumbComponent extends PureComponent {
      render() {
        return <div> {this.props.value} </div>;
      }
    }

之后,组件就仅在props值改变了才会重绘。

需要注意的是,PureComponent仅会对props作简单的比较,如果用到复杂的数据结构,可能会遗漏props数据的改变而导致组件未被更新。

实现 shouldComponentUpdate

shouldComponentUpdate是一个组件方法在props或者state值被改变,执行render之前被调用的方法。如果shouldComponentUpdate返回true,render就会被调用,如果返回false,就不会被调用。通过实现这个方法,你可以指导React避免特定组件在props值未发生改变进行重绘。举个例子,在上例中的dumb component实现shouldComponentUpdate

class DumbComponent extends Component {
  shouldComponentUpdate(nextProps) {
    if (this.props.value !== nextProps.value) {
      return true;
    } else {
      return false;
    }
  }
render() {
    return <div>foo</div>;
  }
}
感谢sturloly为本文提供“仅需”两字
编辑于 2017-07-20 19:34