06-类组件的生命周期(非常重要)

7/24/2023

# 一,类组件的生命周期(非常重要)

# 1,constructor(装载)

vue组件的生命周期,有些并不重要,基本上用不到,但是React的生命周期都非常重要,没有多余的,关于生命周期有非常多的面试题。

只有类组件才会有生命周期钩子函数,函数式组件没有生命周期钩子的函数的。

生命周期函数很多,我们需要掌握6个:

  • 装载阶段(3个):constructor, render, componentDidMount
  • 更新阶段(2个):render,componentDidUpdate
  • 卸载阶段(1个):componentWillUnmount

图示:

1675213974652

1675214090529

现在就一个个学习,先学习constructor,代码如下:

1675214447531

在constructor中,可以完全this的绑定如下:

1675214644928

昨天我们说了两种解决办法:

1675214722265

在官方文档中,还有一种解决办法,就是在constructor中进行this绑定,如下:

1675214864517

在constructor中有一个props,这个props是用来接收父传递的数据的,props的数据流和state的数据流必须独立。下面的代码是不规范的,如下:

1675215355693

在constructor中,不能调用setState方法,如下:

1675215515762

在constructor中,不能调用接口,不能进行DOM操作,不能开定时器... 一切和业务相关的代码都不能写在constructor钩子函数中。

总结在constructor中,可以做什么:

  • 定义状态
  • 绑定方法的this

# 2,render(装载)

作用:用于返回组件的视图结构,这个结构在vue叫模板,在react中叫jsx。它的背后是生成一棵Fiber树,在vue中叫虚拟DOM,在React叫Fiber树,早期的React也是使用的虚拟DOM。

在render函数中,不能调用setState,如下:

1675215951068

上面的写法是,你直接调用了setState,有时候,你会间接地调用setState,如下:

1675216171127

浏览器死循环了,如下:

1675216186006

render是一个函数,这个函数返回一个jsx,jsx叫jsx元素,本质是一个对象,创建jsx元素有2种方法,一种是直接使用jsx,另外一种是React.creeateEleement。也就说,调用render函数,会生成棵Fiber树,类似于Vue中的虚拟DOM,这个Fiber树是双向链表结构。生成这课Fiber树的过程是异步的,生成的过程是可以中断,能不能中断是看浏览器忙不忙,如果浏览器比较忙,就可能中断,等待浏览器不忙时,会继续生成,直到Fiber树创建完成。然后会进行协调运算,这个协调运算类似于Vue中的Diff运算,也就是一棵老的Fiber树和一棵新的Fiber树进行对比运算。运算对比后,就会进入到commmit提交阶段,一次性提交Fiber更新DOM。

# 3,componentDidMount(装载)

类似于vue中的mounted,表示页面第一次渲染完成。在这个钩子函数中可以做什么?

  • 调用接口
  • 开定时器
  • DOM操作
  • 编写业务逻辑

代码如下:

1675217665707

# 4,render(更新)

什么时候进入到更新阶段,如下:

1675217751176

代码如下:

1675217859243

# 5,componentDidUpdate(更新)

相当于Vue中的updated,表示页面再次渲染成功。

监听器是用来监听数据是否变化,updated表示数据变化了,会执行updated,也就是说数据变化了,在updated也可以监听到了。要监听数据变化,在vue中,使用监听器比使用updated更方便。在react中是没有监听器的概念的,在React中实现类似于Vue的监听器的功能,需要使用compoentDidUpdate钩子函数了。

上代码:

1675218153932

如果不使用componentDidUpdate,还有没有办法实现类似于Vue中的监听器?

答:this.setState({}/fn, callback) 利用callback也可以感知到数据变化了。推荐使用componentDidUpdate。因为多个setState会合并,合并后,callback也会出问题。代码如下:

1675218384742

在componetDidUpdate中,尝试调用setState,如下:

1675218546959

你要想在componentDidUpdate中调用setState,需要给出一个出口,不给出口,直接爆栈了,给出出口,如下:

1675218813251

使用定时器把setState包起来如下:

1675218964887

强调一下,React实现Vue中的监听器。Vue听监听器写法如下:

watch: {
    page() {
        axios.then(res => {
            this.list = res.list;
        })
    }
}

在Vue中如果不使用watch, 可以使用updated代替。 但是在React没有watch, 只能使用componentDidUpdate来实现, 或使用setState中的callback来实现。
1
2
3
4
5
6
7
8
9

num变化了,我需要监听到num变化了,如下:

1675219420275

总结:

  • componentDidUpdate中可以调用setState,但是必须给出出口(终止条件),否则会产生死循环,循环到一定次数就会报错。
  • componentDidUpdate可以模拟vue中的监听器,特别需要注意终止条件。
  • 除了使用componentDidUpdate实现监听器之外,还可以使用this.setState中的callback来实现,不建议使用,因为setState会合并,callback容易出问题。

# 6,componentWillUnmount(卸载)

类似于Vue中的beforeDestroy,表示组件即将销毁。在这里我们可以:

  • 清缓存
  • 清除定时器
  • 关闭长连接
  • 销毁DOM元素
  • ....

# 7,shouldComponentUpdate(了解)

它是控制更新阶段的开关,说白了,就是来控制是否更新,当返回true就正常更新,当返回false时就不更新。

在项目中用的不多,是官方提供的一种性能优化方案。

1675221332401

代码如下:

1675221446044

默认情况下,没有写的话,就是返回true,看一下state参数,如下:

1675221524736

shouldComponentUpdate,返回true时,正常更新,返回false时,不执行更新阶段。注意细:

  • 当执行forceUpdate时,会绕过shouldComponentUpdate方法,一定会进入到更新阶段。
  • shouldComponentUpdate,可以使用PureCompoentf替代

1675222166690

为什么要使用这个开关呢?

答:组件中有很多状态,有些状态会参与到界面刷新,也就是说有些状态变了,需要更新页面。但是还有一些状态是不参与到界面更新,也就是状态变了,不需要更新页面的,此时就体现出开关的重要性了。参与页面更新的状态,状态变化了,在showCompoentUpdate中返回true,正常更新。如果没有参与页面刷新的状态变化了,在shouldCompoentUpdate中返回false,就需要再次调用render。这样,就会少生成一次Fiber树。这个钩子函数是用来性能调优的,可以阻塞掉那些不参与视图渲染的状态更新导致的Fiber生成。

React组件渲染(更新)流程,由两个阶段组成的,一个叫render阶段,一个叫commit阶段,如下:

1675222243891

render阶段:

  • 目标是生成Fiber树,这个过程是异步的,是可中断,并且不会执行任何副作用。到底中断与否,看的是浏览器主线程的忙不忙。

commit阶段:

  • 目的是把协调运算的结果,一次性提交渲染或更新真实DOM。这个过程在V18之前是不可中断的,在V18中是可以人为中断的。
Last Updated: 2/2/2023, 5:44:28 PM