文章同步于Github Pines-Cheng/blogjavascript
React在减小重复渲染方面确实是有一套独特的处理办法,那就是虚拟DOM,但显然在首次渲染的时候React绝无可能超越原生的速度,或者必定能将其它的框架比下去。尤为是在优化前的React,每次数据变更都会执行render,大大影响了性能,特别是在移动端。html
在初始化渲染时,咱们须要渲染整个应用
(绿色 = 已渲染节点)java
咱们想更新一部分数据。这些改变只和一个叶子节点相关(绿色的)react
咱们只想渲染通向叶子节点的关键路径上的这几个节点(绿色的)git
若是你不告诉 React 别这样作,它便会如此
(橘黄色 = 浪费的渲染)程序员
从上图能够看见,组件除了必要渲染的三个节点外,还渲染了其余没必要要渲染的节点,这对性能是一个很大的浪费。若是对于复杂的页面,这将致使页面的总体体验效果很是差。所以要提升组件的性能,就应该想尽一切方法减小没必要要的渲染。github
React的生命周期以下,还没熟悉的同窗能够去熟悉一下。web
由于其中的 shouldComponentUpdate
是优化的关键。React的重复渲染优化的核心其实就是在shouldComponentUpdate里面作数据比较。在优化以前,shouldComponentUpdate
是默认返回true的,这致使任什么时候候触发任何的数据变化都会使component从新渲染。这必然会致使资源的浪费和性能的低下——你可能会感受比较原生的响应更慢。segmentfault
React性能优化的关键在于shouldComponentUpdate
,api
在上面的示例中,由于 C2 的 shouldComponentUpdate
返回 false,React 就不须要生成新的虚拟 DOM,也就不须要更新 DOM,注意 React 甚至不须要调用 C4 和 C5 的 shouldComponentUpdate
。
C1 和 C3 的 shouldComponentUpdate
返回 true,因此 React 须要向下到叶子节点检查它们,C6 返回 true,由于虚拟 DOM 不相等,须要更新 DOM。最后感兴趣的是 C8,对于这个节点,React 须要计算虚拟 DOM,可是由于它和旧的相等,因此不须要更新 DOM。
在传入组件的props和state只有一层时,咱们能够直接使用 React.PureComponent,它会自动帮咱们进行浅比较(shallow-compare),从而控制shouldComponentUpdate的返回值。
可是,当传入props或state不止一层,或者未array和object时,浅比较(shallow-compare
)就失效了。固然咱们也能够在 shouldComponentUpdate()
中使用使用 deepCopy
和 deepCompare
来避免无必要的 render()
,但 deepCopy
和 deepCompare
通常都是很是耗性能的。这个时候咱们就须要 Immutable
。
JavaScript 中的对象通常是可变的(Mutable),由于使用了引用赋值,新的对象简单的引用了原始对象,改变新的对象将影响到原始对象。如
foo={a: 1}; bar=foo; bar.a=2
你会发现此时 foo.a 也被改为了 2。虽然这样作能够节约内存,但当应用复杂后,这就形成了很是大的隐患,Mutable 带来的优势变得得不偿失。为了解决这个问题,通常的作法是使用 shallowCopy
(浅拷贝)或 deepCopy
(深拷贝)来避免被修改,但这样作形成了 CPU 和内存的浪费。
而Immutable 能够很好地解决这些问题。
Immutable Data
就是一旦建立,就不能再被更改的数据。对 Immutable 对象的任何修改或添加删除操做都会返回一个新的 Immutable 对象。Immutable 实现的原理是 Persistent Data Structure
(持久化数据结构),也就是使用旧数据建立新数据时,要保证旧数据同时可用且不变。同时为了不 deepCopy 把全部节点都复制一遍带来的性能损耗,Immutable 使用了 Structural Sharing
(结构共享),即若是对象树中一个节点发生变化,只修改这个节点和受它影响的父节点,其它节点则进行共享。
能够看看下面这个经典的动画:
Immutable.js本质上是一个JavaScript的持久化数据结构的库 ,可是因为同期的React太火,而且和React在性能优化方面完美无缺的配合,致使你们经常把它们二者绑定在一块儿。
Immutable.js是Facebook 工程师 Lee Byron 花费 3 年时间打造,但没有被默认放到 React 工具集里(React 提供了简化的 Helper)。它内部实现了一套完整的 Persistent Data Structure
,且数据结构和方法很是丰富(彻底不像JS出身的好很差)。像 Collection、List、Map、Set、Record、Seq。有很是全面的map、filter、groupBy、reduce、find函数式操做方法。同时 API 也尽可能与 Object 或 Array 相似。 Immutable.js 压缩后下载有 16K。
其中有 3 种最重要的数据结构说明一下:(Java 程序员应该最熟悉了)
Map
:键值对集合,对应于 Object,ES6 也有专门的 Map 对象
List
:有序可重复的列表,对应于 Array
Set
:无序且不可重复的列表
简单示例
import { Map } from "immutable"; const map1 = Map({ a: 1, b: 2, c: 3 }); const map2 = map1.set('b', 50); map1.get('b'); // 2 map2.get('b'); // 50
seamless-immutable是另外一套持久化数据结构的库,它并无实现完整的 Persistent Data Structure
,而是使用 Object.defineProperty
(所以只能在 IE9 及以上使用)扩展了 JavaScript 的 Array 和 Object 对象来实现,只支持 Array 和 Object 两种数据类型,API 基于与 Array 和 Object ,所以许多不用改变本身的使用习惯,对代码的入侵很是小。同时,它的代码库也很是小,压缩后下载只有 2K。
简单示例
// 使用 seamless-immutable.js 后 import Immutable from 'seamless-immutable'; var array = Immutable(["totally", "immutable", {hammer: "Can’t Touch This"}]); array[1] = "I'm going to mutate you!" array[1] // "immutable" array[2].hammer = "hm, surely I can mutate this nested object..." array[2].hammer // "Can’t Touch This" for (var index in array) { console.log(array[index]); } // "totally" // "immutable" // { hammer: 'Can’t Touch This' } JSON.stringify(array) // '["totally","immutable",{"hammer":"Can’t Touch This"}]'
seamless-immutable的实现依赖于ECMAScript 5 的一些特性,如Object.defineProperty 和 Object.freeze,所以会在浏览器兼容性方面有所欠缺:
不过这不是问题啦,可使用 polyfill es-shims/es5-shim 来解决。
虽然 Immutable.js
尽可能尝试把 API 设计的原生对象相似,有的时候仍是很难区别究竟是 Immutable 对象仍是原生对象,容易混淆操做。
Immutable 中的 Map 和 List 虽对应原生 Object 和 Array,但操做很是不一样,好比你要用 map.get('key')
而不是 map.key
,array.get(0)
而不是 array[0]
。另外 Immutable 每次修改都会返回新对象,也很容易忘记赋值。
当使用外部库的时候,通常须要使用原生对象,也很容易忘记转换。
固然也有一些办法来避免相似问题发生:
使用 Flow 或 TypeScript 这类有静态类型检查的工具
约定变量命名规则:如全部 Immutable 类型对象以 $$ 开头。
使用 Immutable.fromJS
而不是 Immutable.Map
或 Immutable.List
来建立对象,这样能够避免 Immutable 和原生对象间的混用。
可是还有一个致命的问题是,对现有代码的改造,使用 Immutable.js 成本实在太大。
而seamless-immutable
虽然数据结构和API不如Immutable.js
丰富,可是对于只想使用Immutable Data来对React进行优化以免重复渲染的咱们来讲,已是绰绰有余了。并且Array和Object原生的方法等均可以直接使用,原有项目改动极小。
因为seamless-immutable的实现依赖于ECMAScript 5 和原生的Array、Object自然的兼容性,致使其在React中的使用很是简单,只要注意三点就能够达到效果:
初始化state数据的时候,使用Immutable的初始化方式。
import Immutable from 'seamless-immutable'; state: { orderList: Immutable([]), }
修改state数据的时候,一样也要注意:
saveOrderList(state, {payload: items}) { return {...state, orderList: Immutable(items)}; }
使用pure-render-decorator,真是方便、快捷又优雅。固然,因为decorator属于ES7的特性,babel还须要本身配置。
import React from 'react'; import pureRender from 'pure-render-decorator'; @pureRender class OrderListView extends React.Component { render() { const {orderList} = this.props; return ( <div> { orderList.map((item) => { return ( <div key={item.orderNum}> <div>{item.orderNum}</div> <div>{item.createTime}</div> <div>{item.contact}</div> <hr/> </div> ); }) } </div> ); } } export default OrderListView;
怎么样,传说中的React的SCU的优化就是这么简单,赶忙去试试吧。