首页 男生 其他 深入浅出React和Redux

2.3.2 更新过程

深入浅出React和Redux 程墨 7672 2021-04-06 02:29

  您可以在百度里搜索“深入浅出React和Redux 艾草文学(www.321553.xyz)”查找最新章节!

  

  2.3.2 更新过程

  当组件被装载到DOM树上之后,用户在网页上可以看到组件的第一印象,但是要提供更好的交互体验,就要让该组件可以随着用户操作改变展现的内容,当props或者state被修改的时候,就会引发组件的更新过程。

  更新过程会依次调用下面的生命周期函数,其中render函数和装载过程一样,没有差别。

  ·componentWillReceiveProps

  ·shouldComponentUpdate

  ·componentWillUpdate

  ·render

  ·componentDidUpdate

  有意思的是,并不是所有的更新过程都会执行全部函数,下面会介绍到各种特例。

  1.componentWillReceiveProps(nextProps)

  关于这个componentWillReceiveProps存在一些误解。在互联网上有些教材声称这个函数只有当组件的props发生改变的时候才会被调用,其实是不正确的。实际上,只要是父组件的render函数被调用,在render函数里面被渲染的子组件就会经历更新过程,不管父组件传给子组件的props有没有改变,都会触发子组件的componentWill-ReceiveProps函数。

  注意,通过this.setState方法触发的更新过程不会调用这个函数,这是因为这个函数适合根据新的props值(也就是参数nextProps)来计算出是不是要更新内部状态state。更新组件内部状态的方法就是this.setState,如果this.setState的调用导致component-WillReceiveProps再一次被调用,那就是一个死循环了。

  让我们对ControlPanel做一些小的改进,来体会一下上面提到的规则。

  我们首先在Counter组件类里增加函数定义,让这个函数componentWillReceive-Props在console上输出一些文字,代码如下:

  componentWillReceiveProps(nextProps) {

  console.log('enter componentWillReceiveProps ' + this.props.caption)

  }

  在ControlPanel组件的render函数中,我们也做如下修改:

  render() {

  console.log('enter ControlPanel render');

  return (

  . . .

  this.forceUpdate() }>

  Click me to repaint!

  );

  }

  除了在ControlPanel的render函数入口处增加console输出,我们还增加了一个按钮,这个按钮的onClick事件引发一个匿名函数,当这个按钮被点击的时候,调用this.forceUpdate,每个React组件都可以通过forceUpdate函数强行引发一次重新绘制。

  注意

  类似上面的代码,在JSX用直接把匿名函数赋值给onClick的方法,看起来非常简洁而且方便,其实并不是值得提倡的方法。因为每次渲染都会创造一个新的匿名方法对象,而且有可能引发子组件不必要的重新渲染,原因在后面的章节会有详细介绍。

  在网页中,我们去点击那个新增加的按钮,可以看到浏览器的console中有如下的输出:

  enter ControlPanel render

  enter componentWillReceiveProps First

  enter render First

  enter componentWillReceiveProps Second

  enter render Second

  enter componentWillReceiveProps Third

  enter render Third

  可以看到,引发forceUpdate之后,首先是ControlPanel的render函数被调用,随后第一个Counter组件的componentWillReceiveProps函数被调用,然后Counter组件的render函数被调用,随后第二第三个函数的这两个函数也依次被调用。

  然而,ControlPanel在渲染三个子组件的时候,提供的props值一直就没有变化,可见componentWillReceiveProps并不是当props值变化的时候才被调用,所以,这个函数有必要把传入参数nextProps和this.props作必要对比。nextProps代表的是这一次渲染传入的props值,this.props代表的上一次渲染时的props值,只有两者有变化的时候才有必要调用this.setState更新内部状态。

  在网页中,我们再尝试点击第一个Counter组件的“+”按钮,可以看到浏览器的console输出如下:

  enter render First

  明显,只有第一个组件Counter的render函数被调用,函数componentWillReceive-Props没有被调用。因为点击“+”按钮引发的是第一个Counter组件的this.setState函数的调用,就像上面说过的一样,this.setState不会引发这个函数componentWillReceive-Props被调用。

  从这个例子中我们也会发现,在React的组件组合中,完全可以只渲染一个子组件,而其他组件完全不需要渲染,这是提高React性能的重要方式。

  2.shouldComponentUpdate(nextProps,nextState)

  除了render函数,shouldComponentUpdate可能是React组件生命周期中最重要的一个函数了。

  说render函数重要,是因为render函数决定了该渲染什么,而说shouldComponent-Update函数重要,是因为它决定了一个组件什么时候不需要渲染。

  render和shouldComponentUpdate函数,也是React生命周期函数中唯二两个要求有返回结果的函数。render函数的返回结果将用于构造DOM对象,而shouldComponent-Update函数返回一个布尔值,告诉React库这个组件在这次更新过程中是否要继续。

  在更新过程中,React库首先调用shouldComponentUpdate函数,如果这个函数返回true,那就会继续更新过程,接下来调用render函数;反之,如果得到一个false,那就立刻停止更新过程,也就不会引发后续的渲染了。

  说shouldComponentUpdate重要,就是因为只要使用恰当,他就能够大大提高React组件的性能,虽然React的渲染性能已经很不错了,但是,不管渲染有多快,如果发现没必要重新渲染,那就干脆不用渲染好了,速度会更快。

  我们知道render函数应该是一个纯函数,这个纯函数的逻辑输入就是组件的props和state。所以,shouldComponentUpdate的参数就是接下来的props和state值。如果我们要定义shouldComponentUpdate,那就根据这两个参数,外加this.props和this.state来判断出是返回true还是返回false。

  如果我们给组件添加shouldCompomentUpdate函数,那就沿用所有React组件父类React.Component中的默认实现方式,默认实现方式就是简单地返回true,也就是每次更新过程都要重新渲染。当然,这是最稳妥的方式,大不了浪费一点,但是绝对不会出错。不过若我们要追求更高的性能,就不能满足于默认实现,需要定制这个函数shouldComponentUpdate。

  让我们尝试来给Counter组件增加一个shouldComponentUpdate函数。先来看看props,Counter组件支持两个props,一个叫caption,一个叫initValue。很明显,只有caption这个prop改变的时候,才有必要重新渲染。对于initValue,只是创建Counter组件实例时用于初始化计数值,在组件实例创建之后,无论怎么改,都不应该让Counter组件重新渲染。

  再来看看state,Counter组件的state只有一个值count,如果count发生了变化,那肯定应该重新渲染,如果count没变化,那就没必要了。

  现在,让我们给Counter组件类增加shouldComponentUpdate函数的定义,代码如下:

  shouldComponentUpdate(nextProps, nextState) {

  return (nextProps.caption !== this.props.caption) ||

  (nextState.count !== this.state.count);

  }

  现在,只有当caption改变,或者state中的count值改变,shouldComponent才会返回true。

  值得一提的是,通过this.setState函数引发更新过程,并不是立刻更新组件的state值,在执行到到函数shouldComponentUpdate的时候,this.state依然是this.setState函数执行之前的值,所以我们要做的实际上就是在nextProps、nextState、this.props和this.state中互相比对。

  我们在网页中引发一次ControlPanel的重新绘制,可以看到浏览器的console中的输出是这样:

  enter ControlPanel render

  enter componentWillReceiveProps First

  enter componentWillReceiveProps Second

  enter componentWillReceiveProps Third

  可以看到,三个Counter组件的render函数都没有被调用,因为这个刷新没有改变caption的值,更没有引发组件内状态改变,所以完全没有必要重新绘制Counter。

  对于Counter这个简单的组件,我们无法感觉到性能的提高,但是,实际开发中会遇到更复杂更庞大的组件,这种情况下避免没必要的重新渲染,就会大大提高性能。

  3.componentWillUpdate和componentDidUpdate

  如果组件的shouldComponentUpdate函数返回true,React接下来就会依次调用对应组件的componentWillUpdate、render和componentDidUpdate函数。

  componentWillMount和componentDidMount,componentWillUpdate和componentDid-Update,这两对函数一前一后地把render函数夹在中间。

  和装载过程不同的是,当在服务器端使用React渲染时,这一对函数中的Did函数,也就是componentDidUpdate函数,并不是只在浏览器端才执行的,无论更新过程发生在服务器端还是浏览器端,该函数都会被调用。

  在介绍componentDidMount函数时,我们说到可以利用componentDidMount函数执行其他UI库的代码,比如jQuery代码。当React组件被更新时,原有的内容被重新绘制,这时候就需要在componentDidUpdate函数再次调用jQuery代码。

  读者可能会问,componentDidUpdate函数不是可能会在服务器端也被执行吗?在服务器端怎么能够使用jQuery呢?实际上,使用React做服务器端渲染时,基本不会经历更新过程,因为服务器端只需要产出HTML字符串,一个装载过程就足够产出HTML了,所以正常情况下服务器端不会调用componentDidUpdate函数,如果调用了,说明我们的程序有错误,需要改进。 深入浅出React和Redux

目录
设置
手机
书架
书页
评论