组件的生命周期是指组件从被创建到挂载到页面中运行起来,再到组件不用时卸载的过程,只有类组件才有生命周期(类组件 实例化 函数组件 不需要实例化)
生命周期新版本和旧版本的对比图如下:
生命周期(constructor())
类组件继承了react Component这个基类,也就继承这个react的基类,才能有render(),生命周期等方法可以使用,这也说明为什么函数组件不能使用这些方法的原因。
constructor():它的入参是props(由父作用域传递过来的自定义属性和children[组件内部嵌套的视图结构]) 第一行代码必须是super(props),调用父类的构造器函数
- 组件自有的state只能在这里定义(先定义后使用),state就是所谓的声明式变量
- 在这里不能修改props,在这里也不能用props来做运算(因为constructor在声明周期中只发生一次)。
- 在这里不要把props和state进行交叉赋值,在React代码逻辑中,永远要保持props和state的独立性。
- 在这里不要使用this.setState来修改state。刚赋完值就进行render,而在constructor中,render生命周期都还没有生成。
- 一般情况下,不要在这里写业务逻辑。有时候我们需要改变this指向时候唯一允许这么做,比如DOM、BOM操作等不要在这里做。
- 一般继承父类、定义状态、改变this指向。
生命周期(挂载阶段)
钩子 函数 | 触发时机 | 作用 |
constructor | 创建组件时,最先执行,初始化的时候只执行一次 | 1. 初始化state 2. 创建 Ref 3. 使用 bind 解决 this 指向问题等 |
render | 每次组件渲染都会触发 | 渲染UI(注意: 不能在里面调用setState() ) |
componentDidMount | 组件挂载(完成DOM渲染)后执行,初始化的时候执行一次,在浏览器更新视图之前调用 | 1. 发送网络请求 2.DOM操作 |
componentWillMount():react 16.3之前
在组件挂载到DOM前调用,且只会被调用一次,在这边调用this.setState不会引起组件重新渲染,也可以把写在这边的内容提前到constructor()中,所以项目中很少用。
componentDidMount():react 17之后
- 相当于Vue中的mounted(),表示挂载阶段已完成,这个声明周期只执行一次。
- 各种业务逻辑(DOM、ref、掉接口、开定时器等等都可以在这里做)
- 在这里可以使用多次this.setState(),默认是异步的。
- 这个声明周期是在更新阶段的,发生在render之后
生命周期(更新阶段)
react 16.3之前
- componentWillReceiveProps(nextProps,nextState)
- 这个生命周期主要为我们提供对props发生改变的监听,如果你需要在props发生改变后,相应改变组件的一些state,在这个方法中改变state不会二次渲染,而是直接合并state。
- shouldComponentUpdate(nextProps,nextState)
- 会返回一个布尔值,判断是否需要更新渲染组件,返回false,不会再向下执行生命周期,是优化react应用的主要手段之一,在这个方法中不能调用setState(),会导致循环调用
- componentWillUpdate
- 这个生命周期用来处理Dom发生更新之前的事情,在这个阶段不可以调用setState,会导致循环调用
- render
- componentDidUpdate(preProps,preState)
- 此时已经完成渲染,Dom和state都已经发生变化,参数都是上一个状态的值
react 17之后
- getDerivedStateFromProps(nextProps,preState)
- 可以返回一个对象用来更新state。
- shouldComponentUpdate render getSnapshotBeforeUpdate(preProps,preState)
- 在这个阶段可以拿到上一个状态Dom元素的坐标、大小等相关信息。用于替代旧的生命周期中的componentWillUpdate
- componentDidUpdate的第三个参数出现。
- 在最近一次渲染提交到DOM树之前执行,可以用来获取更新前的DOM信息。
- componentDidUpdate(preProps,preState,snapshot)
- 首次渲染不会调用,组件更新后立即调用,在这个生命周期中使用this.setState时必须将其包裹在条件语句中,否则会导致死循环。
更新阶段会在三种情况下触发:
1、父组件更改props:一个组件并不能主动更改它拥有的props属性,它的props属性是由它的父组件传递给它的。强制对props进行重新赋值会导致程序报错。
- 直接使用,每当父组件重新render导致的重传props,子组件将直接跟着重新渲染,无论props是否有变化。可通过shouldComponentUpdate方法优化。
- 在componentWillReceiveProps方法中,将props转换成自己的state
2、更改state:state的更改是通过setState接口实现的。组件的更新原因很大一部分是因为调用setState接口更新state所致,我们常常以同步的方式调用setState,但实际上setState方法是异步的。
- 组件本身调用setState,无论state有没有变化。可通过shouldComponentUpdate方法优化。
3、调用forceUpdate方法:强制组件进行更新。
生命周期(卸载阶段)
钩子 函数 | 触发时机 | 作用 |
componentWillUnmount | 组件卸载(从页面中消失) | 执行清理工作(比如:清理定时器等) |
在新版本中,某些生命周期废弃原因: componentWillMount、componentWillReceiveProps、componentWillUpdate这三个生命周期钩子都是在render阶段执行的
(1)在fiber架构被应用之前,render阶段是不能被打断的,页面复杂以后,就有可能阻塞页面的渲染;
(2)于是react推出fiber架构,使原本同步的渲染过程变成异步的,将一个大的更新任务拆解成许多小任务,低优先级任务的render阶段可以被高优先级任务打断;
(3)这就导致在render阶段执行的生命周期函数会被多次调用,如果在这些函数中执行一些带有副作用的操作,比如发送网络请求,就会导致一个同样的网络请求被调用多次,因此需要一个新的生命周期去解决这个问题;
(4)用静态函数getDerivedStateFromProps来取代被废弃的几个生命周期函数,开发者就无法通过this获取到组件实例,也不能发送网络请求或调用this.setState,通过强制开发者在render之前只做无副作用的操作来避免对生命周期的滥用。