React学习:组件的生命周期

一、什么是生命周期?

生命周期就是指一个对象的生老病死。

而组件的生命周期则是这个组件从创建到销毁的一个过程。在这个过程中会有不同的阶段,从而会产生一些对应的生命周期函数来供我们使用,以便能够进行一些渲染、更新等处理。

react的生命周期可以分为三个阶段:

  • 初始化(挂载)
  • 存在期(更新)
  • 销毁 (卸载)

在源码里对应的就是MOUNTING、RECEIVE_PROPS、UNMOUNTING


二、生命周期阶段详解

这里写图片描述
这里写图片描述

1、初始化阶段(挂载)

(1)首先通过constructor方法完成对props和state的初始化。其中props是react从父组件继承过来的,state需要我们自己去定义。

  • getDefaultProps:获取实例的默认props(即使没有生成实例,组件的第一个实例被初始化CreateClass的时候调用,只调用一次,)
  • getInitialState:获取每个实例的初始化state(每个实例自己维护)

(2)之后是componentWillMount(),这个方法执行的时间点是在props和state初始化之后,render之前。这个时候可以在里面执行this.setState()操作,执行setState()并不会导致页面的渲染,只是单纯的state的合并操作。这个方法只执行一次(看图:初始化阶段的方法都是只执行一次的)。

  • componentWillMount:组件即将被装载、渲染到页面上

(3)render:返回JSX,这个JSX会调用React.createElement(),生产虚拟DOM节点,之后再经过一系列复杂的过程,成为真实DOM(只能通过this.props和this.state访问数据;不允许修改状态和DOM输出;可以返回null或者false,就是什么都不渲染

(4)componentDidMount:这时已经挂载到了DOM上,所以在这个方法里面可以操作DOM。此时已可以使用其他类库来操作这个DOM。在服务端中,该方法不会被调用。
当我们需要请求外部接口数据,数据请求一般都在这里处理。但是在这里写一堆ajax你不觉得很混乱吗,交给redux吧。

有些人还习惯在constructor或者componentWillMount中,进行数据请求,认为这样可以更快的获取到数据,但它们相比componentDidMount的执行时间,提前的时间实在是太微乎其微了。另外,当进行服务器渲染时(SSR),componentWillMount是会被调用两次的,一次在服务器端,一次在客户端,这时候就会导致额外的请求发生。

组件进行数据请求的另一种场景:由父组件的更新导致组件的props发生变化,如果组件的数据请求依赖props,组件就需要重新进行数据请求。例如,新闻详情组件NewsDetail,在获取新闻详情数据时,需要传递新闻的id作为参数给服务器端,当NewsDetail已经处于挂载状态时,如果点击其他新闻,NewsDetail的componentDidMount并不会重新调用,因而componentDidMount中进行新闻详情数据请求的方法也不会再次执行。这时候,应该在componentWillReceiveProps中,进行数据请求:

componentWillReceiveProps(nextProps){
    if(this.props.newId !== nextProps.newsId){
        fetchNewsDetailById(nextProps.newsId)
          // 根据最新的新闻id,请求新闻详情数据
    }
}

如果进行数据请求的时机是由页面上的交互行为触发的,例如,点击查询按钮后,查询数据,这时只需要在查询按钮的事件监听函数中,执行数据请求即可,这种情况一般是不会有疑问的。

2、存在期阶段(更新)

上述的初始化阶段已过去,剩下的都是props和state的改变导致的不断Update的过程,所以说初始化的生命周期函数都只执行一次。

这个时候我们的组件已经渲染出来了,用户也能够看到了,随着用户的操作,比如点击按钮,发送信息等就会导致有新的props或者state从上层甚至上上层流入到我们刚刚新鲜出炉的组件里,这时就会触发组件的更新,导致UI视图的重绘。

  • componentWillReceiveProps:组件将要接收到属性的时候调用(赶在父组件修改真正发生之前,可以修改属性和状态)

从state或props改变后最终都会交汇到一点shouldComponentUpdate,它是生命周期里重绘组件的阀门,它的返回值是一个boolean,默认返回true,如果返回false,那就简单多了,因为接下来的步骤都不会执行了。

  • shouldComponentUpdate:决定是否需要继续执行更新过程
    若返回false:则componentWillUnmount -> 结束
    如果返回true:咱们继续。

    • componentWillUpdate:不能修改属性和状态
      它和componentWillMount差不多,就是重绘前的准备工作。唯一需要注意的是,这里面不能进行setState()或者更新props。因为这个函数自己就会把最新的state和props设置到this.state和this.props中。
      把state和props的更新放到componmentWillReceiveProps里吧。

    • componentDidUpdate:它和componmentDidMount差不多,也是可以获取真实DOM,可以用第三方库操作DOM


3、销毁阶段(卸载)

  • componentWillUnmount:开发者需要来销毁(组件真正删除之前调用,比如计时器和事件监听器)

三、setState的时机

组件的生命周期方法众多,哪些方法中可以调用setState更新组件状态?哪些方法中不可以呢?

可以的方法

componentWillMount、componentDidMount、componentWillReceiveProps、componentDidUpdate

注意:
1、componentWillMount

  • 同步调用setState不会导致组件进行额外的渲染,组件经历的生命周期方法依次是componentWillMount -> render -> componentDidMount,组件并不会因为componentWillMount中的setState调用再次进行更新操作。
  • 异步调用setState,组件是进行额外的更新操作。不过实际场景中很少在componentWillMount中调setState,一般可以通过直接在constructor中定义state的方式代替。

2、一般情况下,当调用setState后,组件会执行一次更新过程,componentWillReceiveProps等更新阶段的方法会再次被调用,但如果在componentWillReceiveProps中调用setState,并不会额外导致一次新的更新过程,也就是说,当前的更新过程结束后,componentWillReceiveProps等更新阶段的方法不会再被调用一次。(注意,这里仍然指同步调用setState,如果是异步调用,则会导致组件再次进行渲染

3、componentDidUpdate中调用setState要格外小心,在setState前必须有条件判断,只有满足了相应条件,才setState, 否组组件会不断执行更新过程,进入死循环(因为setState会导致新一次的组件更新,组件更新完成后,componentDidUpdate被调用,又继续setState,死循环就产生了。)

不可以的方法

其他生命周期方法都不能调用setState,主要原因有两个:

1、产生死循环。
例如,shouldComponentUpdate、componentWillUpdate 和 render 中调用setState,组件本次的更新还没有执行完成,又会进入新一轮的更新,导致不断循环更新,进入死循环。

2、无意义。componentWillUnmount 调用时,组件即将被卸载。实际上,在componentWillUnmount中调用setState也是会抛出异常的。


四、render次数 != 浏览器界面更新次数

先看下面的一个例子:

class App extends React.Component {
  constructor(props){
    super(props);
    this.state = {
     bgColor:'red'
    }
  }
  componentDidMount(){
    this.setState({
      bgColor:'yellow'
    });
  }

  render(){
    var {bgColor} = this.state;
    return(
      <div style={{backgroundColor:bgColor}}>
        Test
      </div>
    );
  }
}

当我们观察浏览器渲染出的页面时,页面中Test所在div的背景色,是先显示红色,再变成黄色呢?还是直接就显示为黄色呢?

答案是:直接就显示为黄色!

这个过程中,组件的生命周期方法被调用的顺序如下:

constructor -> componentWillMount -> render -> componentDidMount -> shouldComponentUpdate -> componentWillUpdate -> render -> componentDidUpdate

组件在挂载完成后,因为setState的调用,将立即执行一次更新过程。虽然render方法被调用了两次,但这并不会导致浏览器界面更新两次,实际上,两次DOM的修改会合并成一次浏览器界面的更新。

组件render的次数 不一定等于 浏览器界面更新次数。虽然JS的执行和DOM的渲染分别由浏览器不同的线程完成,但JS的执行会阻塞DOM的渲染,而上面的两次render是在一个JS事件周期内执行的,所以在两次render结束前,浏览器不会更新界面。

猜你喜欢

转载自blog.csdn.net/b954960630/article/details/79823360