React是一个专注于UI层的框架,它使用虚拟DOM技术,以保证它UI的高速渲染;使用单向数据流,因此它数据绑定更加简单;
那么它内部是如何保持简单高效的UI渲染呢?这种渲染机制有可能存在什么性能问题呢?
React组件渲染问题引出
React不直接操作DOM,它在内存中维护一个快速响应的DOM描述,render方法返回一个DOM的描述,
React能够计算出两个DOM描述的差异,然后更新浏览器中的DOM。这就是著名的DOM Diff。
就是说React在接收到属性(props)或者状态(state)更新时,就会更新UI。所以React整个UI渲染是比较快的。但是这里面可能出现的问题是:
假设我们定义一个父组件,其包含了5000个子组件。我们有一个输入框输入操作,每次输入一个数字,对应的那个子组件背景色变红。
|
|
这样我们输入数字1,则子组件1背景色变化,但是在这个过程中,所有的子组件都进行了重新渲染,导致整体渲染变慢。
造成这种现象的原因是 React中父组件更新默认触发所有子组件更新。
同时,我们经常在遍历列表元素时候会遇到这样的提示:
|
|
这就是我们今天要讨论的两个性能优化点:
|
|
React性能检测工具
安装 react 性能检测工具 npm i react-addons-perf –save,然后在./app/index.jsx中
|
|
检测方法,在浏览器控制台输入如下命令:
|
|
运行程序。在操作之前先运行Perf.start()开始检测,
然后进行若干操作,运行Perf.stop停止检测,
然后再运行Perf.printWasted()即可打印出浪费性能的组件列表。
在项目开发过程中,要经常使用检测工具来看看性能是否正常。
如果性能的影响不是很大,例如每次操作多浪费几毫秒、十几毫秒,个人以为没必要深究,但是如果浪费过多影响了用户体验,就必须去搞定它。
PureRenderMixin 优化
React 最基本的优化方式是使用PureRenderMixin,
安装工具 npm i react-addons-pure-render-mixin –save,然后在组件中引用并使用
|
|
|
|
React 有一个生命周期 hook 叫做shouldComponentUpdate,
组件每次更新之前,都要过一遍这个函数,如果这个函数返回true则更新,如果返回false则不更新。
而默认情况下,这个函数会一直返回true,就是说,如果有一些无效的改动触发了这个函数,也会导致无效的更新
那么什么是无效的改动?之前说过,组件中的props和state一旦变化会导致组件重新更新并渲染,
但是如果props和state没有变化也莫名其妙的触发更新了呢(这种情况确实存在)———— 这不就导致了无效渲染吗?
|
|
因此,我们在开发过程中,在每个 React 组件中都尽量使用PureRenderMixin
PureComponent
React 15.3.0 新增了一个 PureComponent 类,
以 ES2015 class 的方式方便地定义纯组件 (pure component),用于取代之前的 PureRenderMixin。
这个类的用法很简单,如果你有些组件是纯组件,那么把继承类从 Component 换成 PureComponent 即可。
当组件更新时,如果组件的 props 和 state 都没发生改变,render 方法就不会触发,
省去 Virtual DOM 的生成和比对过程,达到提升性能的目的。
虽然React提供了Virtual DOM DOM Diff 等优秀的能力来提高渲染性能,但是在实际使用过程中,我们经常会遇到父组件更新,
不需要更新所以子组件的场景(分页),此时必须考虑利用React本周的渲染机制来进行优化。
相关代码:https://github.com/younth/react-tutorial/tree/master/%E6%80%A7%E8%83%BD%E8%B0%83%E4%BC%98
避免更新Demo
React 使用虚拟 DOM,它是在浏览器中的 DOM 子树的渲染描述,这个平行的描述让 React 避免创建和操作 DOM 节点,
这些远比操作一个 JavaScript 对象慢。当一个组件的 props 或 state 改变,
React 会构造一个新的虚拟 DOM 和旧的进行对比来决定真实 DOM 更新的必要性,
只有在它们不相等的时候,React 才会使用尽量少的改动更新 DOM。
在此之上,React 提供了生命周期函数 shouldComponentUpdate,
在重新渲染机制回路(虚拟 DOM 对比和 DOM 更新)之前会被触发,赋予开发者跳过这个过程的能力。这个函数默认返回 true,让 React 执行更新。
shouldComponentUpdate (nextProps, nextState) {
return true;
}
返回true时就会调用componentDidUpdata() {
}
切住,React 会非常频繁的调用这个函数shouldComponentUpdate(){},所以要确保它的执行速度够快。
假如你有个带有多个对话的消息应用,如果只有一个对话发生改变,如果我们在 ChatThread 组件执行 shouldComponentUpdate,React 可以跳过其他对话的重新渲染步骤。
我们可以简单的实现 shouldComponentUpdate 如下: