[译]高性能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.
如何生效
- 打开app并在访问的链接后面加参数: react_perf 。如:http://localhost:3000?react_perf
- 打开Chrome DevTools的Performance,点击record。
- 执行想要分析的操作
- 停止录制
- 查看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
- 使用npm安装:npm i --save-dev why-did-you-update
- 添加下面的代码片段到你的项目中:
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,查看控制台输出。
应用中的一些组件执行了不必要的渲染方法。试着实现上述的技术来防范一些不必要的渲染。如果完成正确,控制台就没有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应用里安装了一些不必要的组件。
打开上面的链接,然后打开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为本文提供“仅需”两字