性能优化的方法:算法
重点关注的是,React组件的渲染性能优化。数据库
React利用Virtual DOM来提升渲染性能,虽然每次页面更新都是对组件的从新渲染,但并非将以前渲染的内容所有抛弃重来,借助Virtual DOM,React可以计算出DOM树最少的修改,这就是React在默认状况下都渲染很迅速的秘籍。npm
发现浪费的渲染时间,须要在Chrome浏览器中安装React Perf扩展。编程
注意:这里的浪费是计算Virtual DOM的浪费,而不是访问DOM树的浪费。浏览器
“过早的优化是万恶之源” - 高德纳 《计算机编程艺术》缓存
表面上的意思是,除非性能出了问题,否则就不要花时间去优化性能问题。性能优化
真正上的意思是,“咱们应该忘记忽略很小的性能优化,能够说97%的状况下,过早的优化是万恶之源,而咱们应该关心对性能影响最关键的那另外的3%的代码” - 高德纳。数据结构
高德纳认为,不要将性能优化的经历浪费在对总体性能提升不大的代码,而是对性能有关键的影响的部分,优化并不嫌早。函数
“过早的优化”:没有任何量化证据的状况下,开发者对性能优化的猜想,并无可测量的性能指标,就彻底不知道当前的性能瓶颈在何处,完成优化以后,也没法知道性能优化是否达到了预期的结果。性能
shouldComponentUpdate的默认实现是一种兜底的保险方法。可是要达到更好的性能,有必要定义shouldComponentUpdate函数。
React-Redux经过提供更好的shouldComponentUpdate的实现方式:
在对比prop和上次渲染所用prop方面上看,依然使用的是“浅层比较”(shallow compare)。能够简单的理解为,JS 的“===”操做符。
想让React-Redux认为先后的对象prop是相同的,就必须保证prop指向的JS对象是一致的。
和单个React组建的生命周期同样,多个React组件也要考虑三个阶段:装载、更新、卸载。
装载阶段:
React组件往下的全部子组件,都须要完全渲染一次,都要经历一遍React组件的装载生命周期。所以,没有多少性能优化的东西。
卸载阶段:
只有一个生命周期函数componentWillUnmount,只是清理componentDidMount,作的事情比装载阶段还少,也没有什么可优化的空间。
值得关心的过程,只剩下更新阶段。
在装载的过程当中,React经过render方法在内存中产生了一个树形结构,树上每个节点表明一个React 组件或者原生DOM元素,这个树形结构就是所谓的Virtual DOM。React根据这个Virtual DOM 来渲染浏览器的DOM树。
Reconciliation(调和):
在更新阶段巧妙地原有的Virtual DOM和新生成的Virtual DOM,找出二者的不一样之处。根据不一样来修改DOM树,更新中这个“找不一样”的过程就叫作Reconciliation(调和)。
Reconciliation算法:
当React要对比两个Virtual DOM的树形结构时,从根节点开始递归往下对比,在这个树形结构中,每一个节点均可以看做当前节点如下部分子树的根节点。因此这个对比算法能够从Virtual DOM上任何一个节点开始执行。
React首先检查两个树形的根节点的类型是否相同,根据相同或者不一样有不一样处理方式。
若是树形结构根节点类型不相同,直接认为原来的树形结构已经没有用,须要构建新的DOM树。原有的树形上React组件会经历“卸载”的生命周期。这种方式可能形成某种程度的浪费,可是为了不较大的复杂度,React必须选择一个更简单跟快捷的算法。
也就是说,对于Virtual DOM树这是一个“更新”的过程,可是却可能引起这个树结构上某些组件的“装载”和“卸载”过程。
节点类型相同的状况
若是两个树形结构的根节点类型相同,React就认为原来的根节点只须要更新过程,不会将其卸载,也不会引起根节点的从新装载。
区分节点的类型:
React会保留节点对应的DOM元素,对树形根节点上的属性和内容作对比,只更新修改的部分。
React组件,利用React库定制的类型;
React根据新节点的props去更新原来根节点的组件实例,引起组件实例的更新过程,也就是按照顺序引起下列函数:
在这个过程当中,若是shouldComponentUpdate函数返回false,那么更新过程中止。为了保持最大的性能,每一个人React组件必需要重视shouldConponentUpdate,若是发现没有必要从新渲染,那么直接返回false。 在处理完根节点的对比以后,React的算法会对根节点的每一个子节点重复同样的动做,这时候每一个子节点就成为它所覆盖部分的根节点,处理方式和它的父节点彻底同样。
当一个组件包含多个子组件的状况,React的处理方式也很是直接。
React选择了看起来很傻的办法,不是寻找两个序列的精确差异,而是直接挨个比较每一个子组件。
若是在代码中明确地告诉每一个组建的惟一标识,就能够帮助React在处理这个问题时聪明不少,告诉每一个组件“身份证号”的途径就是key属性。
在一列子组件中,每一个子组件的key值必须惟一,否则就没有帮助React区分各个组件的身份。
若是key值不惟一,就会误导React作出错误的判断,甚至致使错误的渲染结果。
注意:虽然key是个prop,可是接受能够的组件并不能读取到key的值,由于key和ref是React保留的两个特殊prop,并无预期让组件直接访问。
reselect库的工做原理:只要相关状态没有改变,那就直接使用上一次的缓存结果。
reselect的计算过程分为两个步骤:
使用reselect须要安装对应的npm包:
$ npm install --save reselect
Redux要求每一个reducer不能修改state状态,若是要返回一个新的状态,就必须返回一个新的对象。
Redux的状态树应该设计的尽可能扁平,使用reselect以后,状态树的设计应该尽可能范式化(Normalized)。
范式化:就是遵守关系型数据库的设计原则,减小冗余数据。
范式化的数据结构就是要让一份数据只存储一份,数据冗余形成的后果就是难以保证数据一致性。
反范式化是利用数据冗余来换取读写效率。
反范式化数据结构的特色就是读取容易,修改比较麻烦。
对比反范式化和范式化方式的优劣,不能看出范式化更加合理。由于虽然join数据须要花费计算时间,可是应用reselect以后,大部分状况下都会命中缓存,实际也就是没有花费不少计算时间。