React学习之进阶性能优化(十五)

来源:互联网 发布:htcg5软件 编辑:程序博客网 时间:2024/05/19 02:01

通常,React通常会在渲染到视图中去的时候,会使用几种特殊的技术最小化要更新的DOM节点从而实现性能优化,尽管在大部分的应用中,React为了更快的引导用户使用,而没有做太多的优化工作,然而这些技术的使用性是必要的。

1.尽量避免更新

React建立和将内部的数据呈现给视图的过程中,React做了如下的事情:通过在真实DOMReact应用之间建立一个过渡层,这个过渡层有时被称为”虚拟DOM“。虽然不是DOM,但是有着和DOM一样的机制处理,几乎所有的操作都会先在虚拟DOM上进行处理,最后才更新到DOM节点上的

当组件的属性或者状态发生改变时,React会比较旧的DOM和新的DOM是不是不同,来确定更新当前DOM节点的必要性,如果不同的话,React就会更新这个DOM节点

在我们写代码的过程中我们可以通过一个函数去控制当前的组件是否需要进行更新,什么时候一定要更新,什么时候不更新。

这个函数叫做shouldComponentUpdate生命周期更新函数,这个函数会发生在重新render视图渲染之前,此函数在React中默认是返回true,然后会执行render函数进行更新。

shouldComponentUpdate(nextProps, nextState) {  return true;}

如果你已经确定了在某一个种情况视图不需要更新,就可以返回false,这会跳过render函数和它的子组件的render函数

2.shouldComponentUpdate的更新行为

在下面这张图中,SCU表示shouldComponentUpdatereturn情况,vDOMEq表示要更新到视图中的React元素是否相等

这里写图片描述

C2之前shouldComponentUpdate返回了false,所以C2包括C4C5都不会进行更新

对于C1C3shouldComponentUpdate返回了true,所以React会递归到更深的地方去检查他们是否需要更新,C6shouldComponentUpdate返回了true,并且旧元素和新元素不同即vDOMEq为红色,所以一定会更新DOM

最后的C8则是vDOMEq为绿色,旧元素和新元素没有变化,所以不会更新

整颗树下来,React更新的节点其实静仅仅是C6C3之所有也更新都是因为内部元素C6要更新,所以实际上我们需要更新的数据要少的很多,性能也就非常必要了

即使我们进行性能优化是非常有必要的,然而我们为了单单减少更新次数而增加我们的代码量是得不偿失的,所以React由专门提供一个父类来完成这件事情。

React.PureComponent

class CounterButton extends React.PureComponent {  constructor(props) {    super(props);    this.state = {count: 1};  }  render() {    return (      <button        color={this.props.color}        onClick={() => this.setState(state => ({count: state.count + 1}))}>        Count: {this.state.count}      </button>    );  }}

上面的代码就是自动进行优化了的。
当然这个父类有一点的缺陷,那就是它是引用比较,就是所谓的浅比较,对于对象和数组这些引用型数据就会出问题

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() {    // This section is bad style and causes a 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>    );  }}

上面的功能是想一个数组中增加数据,而我们的底层组件ListOfWords可以实时更新,然而在这份代码中是不起作用的,因为数组是引用型数据,我们比较数组==号时,是相等的,所以不会进行更新,某种情况下,我们依旧需要直接重写shouldComponentUpdate组件。

3.使用函数式编程规范

对于数据操作,请使用函数式编程规范,不影响原数据的值。如下

handleClick() {  this.setState(prevState => ({    words: prevState.words.concat(['marklar'])  }));}

上面这份代码中没有改变prevState的值,不要试图用push来处理,现在ES6可以更好的支持函数式编程,比如说之前说到过的扩展运算符...,也可以用在上面那个功能里用

handleClick() {  this.setState(prevState => ({    words: [...prevState.words, 'marklar'],  }));};

两份代码效果是一样的。

比如说你可以直接复制改变某一个对象的值

function updateColorMap(colormap) {  colormap.right = 'blue';}

上述代码不如用如下代码:

function updateColorMap(colormap) {  return Object.assign({}, colormap, {right: 'blue'});}

Object.assign(ES6)函数的用处是将对象colormap中可以被for in枚举的属性复制到{}中并覆盖right属性值为'blue'

这里也可以用扩展运算符

function updateColorMap(colormap) {  return {...colormap, right: 'blue'};}

这个运算进行的是硬复制

4.尽可能的保持变量不变

即为常量,这里也是综合函数式编程的,将参数看成一个常量,我们唯有构造一个新的变量来处理,在我们之前大量的代码中反复用到的const就是用于定义常量的,这是为了数据的安全性,和有效的控制数据变化

同时我们要尽可能让变量之间的共享性变小,比如说

const x = { foo: "bar" };const y = x;y.foo = "baz";x === y; // true

上述代码中xy共用了内存,进行了分享,隔离变量共享是为了有利我们的shouldComponentUpdate函数处理,当我们用PureComponent进行性能优化时,我们要消除引用型数据的干扰,进行变量共享分离是必要的,有一些插件可以非常快捷的实现这些功能,比如:Immutable.js

下一篇将讲React不用ES6语法实现

1 0
原创粉丝点击