react性能优化

1,735 阅读5分钟

如果想提高性能就需要知道什么地方会影响性能?

从过往的经验和实践中,影响网页性能最大的因素就是浏览器的重绘和回流,React背后的虚拟DOM就是尽可能的减少浏览器的重绘和回流。

那在此之上,我们还能做什么来防止不必要的渲染?

在这之前,先跟我一起了解下函数式编程

什么是函数式编程?

函数式编程起源于数学,假设我们定义一个加法的方法f,然后改变输入为f(1,3),那么无论这个方法的上下文,无论什么时间调用多少次,返回结果都是4,用数学表达就是f(x,y) = z,给定输入x,y作用在f上,始终结果是z

函数式编程讲究的三个原则:
1.相同的输入得到相同的输出
2.没有副作用
3.不依赖外部的状态(方法内的状态都只在方法的生命周期内存活,不能在方法中使用共享变量,因为这样会给方法带来不可知对的因素)

那说了这么多函数式编程有什么好处呢?

1、函数式编程不依赖外部状态这一特点,使得我们可以利用CPU在分布式集群上做并行计算,这对于多种科学计算和资源密集型计算任务是非常核心的一点,让计算机高效处理这类任务变的可能
2、相同的输入得到相同对的输出,这让我们的代码变得可预测,可以非常方便的进行方法级别的测试。


说完函数式编程,我们再回头说说react的性能优化
《深入React技术栈中》这样描述的“ react的设计是有函数式编程的基因的,react组件本身就是纯函数,react的createElement方法保证了组件是纯净的,即传入相同的props得到一定的虚拟dom,整个过程可预测”。
那么优化的时候我们可以考虑通过拆分组件为子组件,进而对组件进行更细粒度的控制。


那接下来就要考虑如何做更细粒度控制,避免多次无用渲染?

react官方有提供PureComponent类,凡是继承自这个类的组件,react都会默认在shouldComponentUpdate中替你做这样一件事,浅比较你的新传入的props和state和现在的props,state。所谓的浅比较就是只比较props和state中数据的引用地址,引用地址不改变就不会重新渲染。

很显然,问题来了,如果我的数据结构嵌套很深,那岂不是会忽略掉深层次的数据变化,而导致页面不渲染? 因此react官方提醒道,继承PureComponent之前最好props,state的结构简单,对于嵌套深的结构就不要继承PureComponent了


那对于深层次的数据结构就这样放弃使用puerRender了吗?
答案当然不是了,不过先不急着说这个,我们先说对于结果简单的可以继承PureComponent的我们要注意些什么

1、

  <MeetingList style = {{'width':'200px','height':'200px','overFlow':'hidden'}}>
  
  <Item items = {this.props.items.filter(item=>item.val>10)}>

这种直接将props设置成对象或者数组的方式,每次都会触发重新渲染,哪怕值没有改变。

原因在于每次调用react组件都会重新创建组件,就算传入的数组或对象值没有改变,但是引用地址发生改变了。

解决方法,将对象提前赋值为常量,不直接使用字面量即可

2、

  <input onChange = this.change.bind(this)>

设置props方法并通过bind绑定this的方式,每次都会触发重新渲染

原因在于bind会返回一个函数,每次执行bind返回的函数的引用地址改变了

解决办法采用箭头函数

3、

class NameItem extends Component {
    render(){
        return (
        <Item >
         <span>a child</span>
         </Item>
        )
    }
}
 

对于设置了子组件的react组件,每次都会重新渲染。

原因在于,组件编译之后其实是这样的

  <Item children = {React.createElement('span',{},'a child')}>

children引用改变了,导致每次都会重新渲染

解决办法让NameItem(也就是Item的父组件)继承pureComponent,根据浅比较策略不会对Item的children进行深比较,也就不会重新渲染


说完简单的数据结构,我们再来说下对于props和state比较复杂的数据结构应该怎么处理,答案是采用immutable。

简单说下immutable
1.持久化数据存储:对immutable对象进行修改的时候都会返回一个新的immutable对象,也就是使用旧数据创建新数据时,包证了旧数据可用

2.结构化共享:如果树中一个节点变化,则只修改这个节点和受它影响的父节点,其他节点实现共享,避免了深拷贝带来的性能问题

如何使用immutable实现pureRender?

答案是在shouldComponentUpdate中利用immutable进行深比较,抛弃之前的PureComponent的浅比较

import React from 'react';
import {is} from 'immutable';

class App extends Component {
 shouldComponentUpdate(nextProps,nextState){
   const thisProps = this.props || {};
   const thisState = this.state || {};
   if(Object.keys(thisProps).length !== Object.keys(nextProps).length ||
   Object.keys(thisState).length !== Object.keys(nextState).length){
       return true;
   }

   for(const key in nextProps){
     if(nextProps.hasOwnProperty(key) && !is(thisProps[key],nextProps[key])){
       return true;
     }
   }

   for(const key in nextState){
     if(nextState.hasOwnProperty(key) && !is(thisState[key],nextState[key])){
       return true;
     }
   }

   return false;

 }
}