react组件的生命周期

每个组件在网页中有自己的生命周期,生命周期可能会经历如下三个过程:

  • 装载过程(Mount):第一次在DOM树中渲染的过程
  • 更新过程(Update):组件被重新渲染的过程
  • 卸载过程(Unmount):组件从DOM中删除的过程

三种不同的过程,React库会一次调用组件的一些成员函数,这些函数被称为组件的生命周期。所以,要定制一个React组件,实际上就是定制这些生命周期函数。

装载过程

当组件第一次被渲染时,依次调用如下函数:

constructor

在ES6的Class课程中,我们就已经详细的介绍过constructor相当于类的构造函数。比如:

class Button extends React.Component {
    constructor() {
        ...
    }
    ....
}

以上代码的constructor就相当于

function Button() {....}

并不是每个组件都需要创建constructor,无状态的组件就不需要定义构造函数。一个React组件需要创建构造函数,往往因为如下原因:

  • 初始化state:组件生命周期中任何函数都可能需要访问state,constructor作为生命周期中第一个被调用的函数,自然也就成了初始化state的理想之地。
  • 绑定成员函数的this环境

ES6的课程中我们介绍过Class的super:

class ColorPoint extends Point {
  constructor(x, y, color) {
    //  相当于Point.call(this, x, y);
    // 调用父类的constructor(x, y)
    super(x, y);
    this.color = color;
    this.handleClick = this.handleClick.bind(this);
  }
  
  handleClick() {
      ....
  }
}

在子类的构造函数中,只有调用super之后才可以使用this。因为子类实例的构建,是基于对父类实例加工(即子类继承父类 Point.call(this, x, y) ),只有super方法才能返回父类。在上面的例子中,我们通过bind方法,让当前实例中的handleClick函数被调用时,this始终指向当前的组件实例。

render

一个React组件可以不定义其他所有函数,但一定要实现render函数,因为React.Component类对除render之外的生命周期函数都有默认实现。

render函数不做实际的渲染工作,它只是返回一个JSX描述的结构。最终由React来操作渲染过程。如果一个组件不需要做任何渲染,那就让render返回null或false。

render是一个纯函数,完全根据state和props来决定返回的结果,且不可在render中改变state,也就是说不可在render中调用this.setState。

componentWillMount和componentDidMount

componentWillMount和componentDidMount分别在render函数前后调用,正好做render函数前后必要的工作。

componentWillMount发生在“将要装载”的时候,这个时候没有任何渲染结果,我们通常在服务端调用componentWillMount;在浏览器端,能在componentWillMount做的事情,我们都可以放在constructor中去完成。

请看如下代码:

class ControlPanel extends Component {
  render() {
    console.log('ControlPanel render');
    return (
      <div>
        <Counter caption="First"/>
        <Counter caption="Second" />
        <Counter caption="Third" />
      </div>
    );
  }
}

class Counter extends Component {

  constructor(props) {
     console.log('constructor: ' + props.caption);
    ......
  }

  componentWillMount() {
     console.log('componentWillMount:' + this.props.caption)
  }

  render() {
    console.log('render ' + this.props.caption);
    .....
    );
  }
}

结果如下:

由于我们不做服务端渲染,所以在实际开发中,更关注componentDidMount。componentDidMount发生在组件已经被“装载”到DOM树的时候。此时,我们可以获取DOM。如果我们想让React和其他UI库配合,比如jQuery。需要注意的是:render函数被调用之后,componentDidMount并不会被立刻调用。

class ControlPanel extends Component {
  render() {
    console.log('ControlPanel render');
    return (
      <div>
        <Counter caption="First"/>
        <Counter caption="Second" />
        <Counter caption="Third" />
      </div>
    );
  }
}

class Counter extends Component {

  constructor(props) {
     console.log('constructor: ' + props.caption);
    ......
  }

  componentWillMount() {
     console.log('componentWillMount:' + this.props.caption)
  }

  componentDidMount() {
    console.log('componentDidMount:' + this.props.caption)
  }

  render() {
    console.log('render ' + this.props.caption);
    .....
    );
  }
}

大家先想一下结果,然后在看结果图:

我靠!怎么和我们想象的不一样,三个componentDidMount怎么都跑到最后才出现?

原来,render函数本身并不往DOM树上直接渲染,它所做的仅仅是返回JSX。React库要把所有组件返回的结果综合起来,才能知道如何产生对应的DOM修改。

更新过程

当组件被装载到DOM树之后,要提供更好的交互体验,就要让该组件可以随着用户操作改变展现内容。当props和state被修改时,就会引发组件的更新过程。

在16.3版本之前, 更新过程React的生命周期见下图:

据官网介绍,17版本之后,在更新过程中React的生命周期会发生很大的变化,有些函数会被更改甚至废弃,这里我们只介绍最重要且最常用的函数shouldComponentUpdate。

shouldComponentUpdate(nextProps, nextState)

除了render函数之外,shouldComponentUpdate应该是React组件生命周期中最重要的函数。render函数决定了组件该渲染什么,shouldComponentUpdate决定了组件什么时候不需要渲染,这一点对于提高组件性能相当重要!

render和shouldComponentUpdate是React组件生命周期中有且仅有需要返回结果的两个函数。render返回结果用于渲染组件,shouldComponentUpdate返回一个布尔值,告诉组件需不需要继续这次更新。shouldComponentUpdate返回true,则继续更新,调用render函数;返回false,则停止更新,不调用render函数。

shouldComponentUpdate接受两个参数:nextProps和nextState,外加this.props和this.state来判断是返回true还是false。

在前面的例子中,我们点击re-render按钮,输出结果:

现在给counter组件添加shouldComponentUpdate函数:

shouldComponentUpdate(nextProps, nextState) {
    return nextProps.caption !== this.props.caption || nextState.count !== this.state.count
}

再点击re-render按钮,结果如下图:

可见,当counter组件上的caption或count属性不发生变化的时候,counter组件就不会再重复渲染。

卸载过程

React组件卸载过程只涉及一个函数componentWillUnmount。该函数在组件从DOM树上删掉之前调用,所以适合做一些清理工作。componentWillUnmount中的工作往往跟componentDidMount有关。如前面所说的在componentDidMount中引入jQuery创建DOM元素,那么在componentWillUnmount中就需要将这些创建的DOM元素清除掉,否则就会造成内存泄露。

原文地址:https://www.cnblogs.com/renzhiwei2017/p/8796582.html