本文正在参与「金石方案 . 瓜分6万现金大奖」

虚拟DOM 很好,但别贪杯。

React对页面更新都是对组件的从头烘托,将之前烘托的内容悉数再烘托一次。而是运用虚拟DOM来到达最小化修正实在DOM,到达高效的更新。

可是,有时清晰知道烘托成果不会有改动,那么这时对虚拟DOM的核算便是“一次糟蹋”。

所以假如能够在开端核算虚拟DOM之前就能够判别,那样能够干脆不要进行虚拟DOM核算和比较,速度就会更快。

留意,这儿说的糟蹋是核算Virtual DOM的糟蹋,并不是访问DOM树的糟蹋。

单个 React 组件的功能优化

shouldComponentUpdate

作为一个生命周期函数,shouldComponentUpdate 函数则决定“什么时分组件不需求从头烘托”。

这个函数默许回来是true,也便是默许每次更新的时分都要调用一切的生命周期函数。包括调用render函数,依据render函数的回来成果核算虚拟DOM。

当回来false时,就不会进行组件的更新。咱们需求依据本身考虑自定义改写回来false的状况。

<App style={{color:'red'}} onClick={() => sayHello()} />

shouldComponentUpdate函数实现,每一次烘托都会以为style这个prop发生了改动,由于每次都会发生一个新的目标给style,在“浅层比较”中,类似== 先判别目标的指针,这儿创建了新目标{color:'red'},即使里边内容相同、也会以为是传入了一个新目标,即新props。那么App组件会从头烘托。

这儿赋值给onClick的是一个匿名的函数,而且是在赋值的时分发生的。也便是说,每次烘托App组件的时分,都会发生一个新的函数,也会造成App组件会从头烘托。

假如需求做“深层比较”,那便是某个特定组件的行为,需求开发者自己依据组件状况去编写。不过也要谨记,不要简略地递归比较一切层次的字段,由于传递进来的prop目标什么结构是无法意料的。

这儿官网有具体实现方法

shouldComponentUpdate 的效果

React.PureComponent 、 React.memo做“浅层比较”

大部分状况下,能够运用React.PureComponent来代替手写shouldComponentUpdate

但它只进行浅比较,所以当 props 或者 state 某种程度是可变的话,浅比较会有遗漏,那你就不能运用它了。

例如当state是一个目标时,传入给子组件,即使state内部特点值改动。子组件也依然会以为props并未改动而不会更新。这也便是为什么React引荐运用不可变数据

这儿引用官网的例子

class ListOfWords extends React.PureComponent {
  render() {
    return <div>{this.props.words.join(',')}</div>;
  }
}
class WordAdder extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      words: ['marklar']
    };
    this.handleClick = this.handleClick.bind(this);
  }
  handleClick() {
    // 这部分代码很糟,而且还有 bug 
    const words = this.state.words;
    words.push('marklar');
    this.setState({words: words});
  }
  render() {
    return (
      <div>
        <button onClick={this.handleClick} />
        <ListOfWords words={this.state.words} />
      </div>
    );
  }
}

处理方法有许多,可是核心一点便是,不要直接修正本来的state而是要仿制一个新的state在新的state的基础上进行改善。

官网的处理方案

多个React组件的功能优化

多个React组件之间组合的烘托进程。和单个React组件的生命周期相同,React组件也要考虑三个阶段:装载阶段、更新阶段和卸载阶段。

装载阶段和卸载阶段都是组件必须阅历一次的,这无可防止,也没有什么太多能够考虑的点。
那么咱们主要看更新阶段。

React的谐和(Reconciliation)进程

在装载进程中,React经过render方法在内存中发生了一个树形的结构,树上每一个节点代表一个React组件或者原生的DOM元素,这个树形结构便是所谓的Virtual DOM。React依据这个Virtual DOM来烘托发生浏览器中的DOM树。

Reconciliation(谐和)便是React找寻新旧虚拟DOM的不同的进程。

依照核算机科学目前的算法研究成果,比照两个N个节点的树形结构的算法,时间复杂度是O(N3),可是为了下降算法时间复杂度,React做出了部分舍弃。

1.节点类型不同的状况

假如树形结构根节点类型不相同,从头构建新的DOM树,原有的树形上的React组件会阅历“卸载”的生命周期,即使或许这颗树的某些节点能够复用,为了防止O(N3)的时间复杂度,React必须要挑选一个更简略更方便的算法,也就只能采用这种方法。

这儿旧组件会阅历卸载进程,新组件会阅历装载进程。

2.节点类型相同的状况

假如两个树形结构的根节点类型相同,React就以为本来的根节点只需求更新进程,不会将其卸载,也不会引发根节点的从头装载。

依据组件类型进行不同处理

  1. DOM元素组件

对于DOM元素类型,React会保留节点对应的DOM元素,只对树形结构根节点上的特点和内容做一下比对,然后只更新修正的部分。

  1. React组件

React能做的仅仅依据新节点的props去更新本来根节点的组件实例,引发这个组件实例的更新进程。

在处理完根节点的比照之后,React的算法会对根节点的每个子节点重复相同的动作,这时分每个子节点就成为它所覆盖部分的根节点,处理方法和它的父节点完全相同。

3.多个子组件的状况

找出两个子组件序列的不同之处,现有的核算出两个序列差异的算法时间是O(N2)。相同为了下降时间复杂度,React挑选了不是寻觅两个序列的精确不同,而是直接挨个比较每个子组件。

当新增加子组件不是在末尾插入时,子组件之间的次序被打乱了,就会造成旧的子组件不能被复用的境况(只能触发旧组件的更新)。

这儿React又推出了一个Key关键特点来处理这个问题

key

React官网的key

key特点就像身份证相同,每个组件都能够具有一个独一无二的key特点用于标识。

当新增加子组件不是在末尾插入时,子组件之间的次序被打乱了,可是咱们有key特点,所以咱们就能够靠key特点来尽或许复用旧子组件(防止旧组件的更新)。

留意:

key值虽然能够在每个时间都唯一,可是变来变去,那么就会误导React做出过错判别,甚至导致过错的烘托成果。也便是要保证key的值在组件的装载->更新->卸载的进程都得保证共同。

const todoItems = todos.map((todo, index) =>
  // Only do this if items have no stable IDs  <li key={index}>    {todo.text}
  </li>
);

假如列表项目的次序或许会改动,咱们不主张运用索引来用作 key 值,由于这样做会导致功能变差,还或许引起组件状况的问题。能够看看 Robin Pokorny 的深度解析运用索引作为 key 的负面影响这一篇文章。翻译版

假如你挑选不指定显式的 key 值,那么 React 将默许运用索引用作为列表项目的 key 值。

参考

《深入浅出React和Redux》
《React官网》