前言:
你有没有遇到过这样的问题:
- 组件的生命周期有哪些?为什么要有生命周期函数?
- 我应该什么时候去获取后台数据? 为什么很多教程都推荐用componentDidMount? 用componentWillMount会有什么问题?
- 为什么setState写在这里造成了重复渲染多次?
- setState在这里写合适吗?
读完本文希望你能对React的组件生命周期有一定的了解,编写React代码的时候能够更加得心应手,注意本文的生命周期讲的主要是浏览器端渲染,这是后端和全栈的主要使用方式,服务端渲染有些不一样,请注意区分,我们会在文中进行简单说明。
文章目录
一、首先我们先来掌握基本单词
掌握单词后会更好理解和记住生命周期:
单词 | 意思 |
---|---|
constructor | 构造函数 |
component | 组件 |
will | 将要 |
get | 得到 |
default | 默认值 |
initial | 最初的 |
unsafe | 不安全的 |
static | 静态的 |
derived | 衍生的 |
should | 应该 |
update | 更新 |
unmount | 取消挂载 |
receive | 收到 |
snap | 提前 |
shot | 拍摄 |
二、全部生命周期总体分为三个执行阶段:
1.组件挂载
(1)constructor
可以理解为组件的第一个生命周期,一般会在这里初始化组件的内部状态state 或者 进行方法绑定, 如果在这里面要使用this则必须在super()之后,如果在这里面需要使用props,那么需要把props作为参数传入
constructor(props) {
super(props);
this.state = {
color: '#fff',
num: this.props.num,
};
}
如果你不需要初始化状态也不需要绑定handle函数的this,那么你可以不实现constructor函数,由默认实现代替。
(2)static getDerivedStateFromProps
这个生命周期只要父组件重新渲染时就会触发。
设置了这个生命周期就不能设置componentWillMount()
这是react16.3之后新增的一个生命周期,这是一个静态方法。
这个函数会在render函数被调用之前调用,包括第一次的初始化组件以及后续的更新过程中,每次接收新的props之后都会返回一个对象作为新的state,返回null则说明不需要更新state
静态方法没有this所以不能使用setState。
(3)componentWillMount ------------ 17版本即将废除
组件将要挂载,这个生命周期基本上没有什么用,而且react*17版本之后废弃。
如果还想继续使用,可以使用UNSAFE_componentWillMount来代替,只不过不可与static getDerivedStateFromProps 混用。
(4)render
react最重要的步骤【React组件的核心方法】,用于根据状态state和属性props创建虚拟dom,进行diff算法,更新dom树都在此进行。此时就不能更改state了。
这里是合成虚拟DOM,可以理解为,在这里实际上都还没有真实的dom。
我们应该保持该方法的纯洁性,这会让我们的组件更易于理解,只要state和props不变,每次调用render返回的结果应当相同,所以请不要在render方法中改变组件状态,也不要在在这个方法中和浏览器直接交互。
(5)componentDidMount ------------- ajax请求生成DOM
componentDidMount方法会在render方法之后立即被调用,该方法在整个React生命周期中只会被调用一次。React的组件树是一个树形结构,此时你可以认为这个组件以及他下面的所有子组件都已经渲染完了,所以在这个方法中你可以调用和真实DOM相关的操作了。
有些组件的启动工作是依赖 DOM 的,例如动画的启动,而 componentWillMount 的时候组件还没挂载完成,所以没法进行这些启动工作,这时候就可以把这些操作放在 componentDidMount 当中。
这个生命周期就是相当重要的一个生命周期,ajax请求一般都在这里进行。
解决一个大家的疑问,为什么不在componentWillMount里面发请求:
1.要废除掉这个生命周期了
2.跟服务端渲染有关系(同构),如果在 componentWillMount 里获取数据,fetch data会执行两次,一次在服务端一次在客户端,使用 componentDidMount 则没有这个问题。
2.组件更新
分为俩种情况,state改变和props改变
如果state改变,会直接进行到组件更新的第二个shouldComponentUpdate,如果是props改变,会先走static getDerivedStateFromProps 或者 componentWillReceiveProps。
(1)static getDerivedStateFromProps
(1.1)componentWillReceiveProps ----- 17版本即将废除
16.4之前,由于在更新阶段,没有static getDeriveStateFromProps这个生命周期,如果有根据props来生成的state,就需要在这里重新设置一次。因为之前是在constructor里面根据props来初始化的state,constructor只会执行一次,所以要在componentWillReceiveProps来修正state。在新的版本里,static getDerivedStateFromProps这个生命周期不管是在装载还是更新的时候都会触发。因此,componentWillReceiveProps也只会工作到react17.后续想要使用的话可以加前缀UNSAFE_,但不可与static getDerivedStateFromProps一同使用。
(2)shouldComponentUpdate 性能优化
这个生命周期用于react的性能优化,接收俩个参数(nextProps,nextState)通常会根据这俩个参数和this.state,this.props来进行对比,根据比较的结果来return true或者false,如果return的是false,将不会再执行后面的生命周期【也就是不会触发渲染】。
注意这个函数如果返回false并不会导致子组件也不更新。
PS: 该函数通常是优化性能的紧急出口,是个大招,不要轻易用,如果要用可以参考Immutable 详解及 React 中实践 .
(3)componentWillUpdate -----17版本即将废除
没什么卵用
(4)render
和mount阶段一样,生成虚拟DOM
(5)getSnapshotBeforeUpdate ----- 在最近一次渲染输出(提交到DOM节点)之前调用。
它使得组件能在发生更改之前从DOM中捕获一些信息(例如,滚动位置)。此生命周期的任何返回值将作为参数传递给componentDidUpdate的第三个参数。
此方法不常用,笔者就没在实战中使用过,官方文档说它可能出现在UI处理中,如需要以特殊方式处理滚动位置的聊天线程等。
(6)componentDidUpdate
接收三个参数,分别是:preProps, preState, snapshot
只有在组件使用了getSnapshotBeforeUpdate生命周期了才会用getSnapshotBeforeUpdate的返回值作为componentDidUpdate生命周期的第三个参数。否则第三个参数为undefined
会在更新后立即被调用,首次渲染不会触发,你可以在这个方法中进行DOM操作,或者做一些异步调用。
一般用来根据判断preProps与this.props或者preState与this.state进行比较从而发请求或进行其他的一些处理。
3.组件销毁
(1)componentWillUnmount
组件将要销毁,这里一般可以用来清理定时器,解绑某些事件,取消网络请求等等。
5.错误处理
(1)static getDerivedStateFromError ------ 会在子组件抛出错误后被调用
它将抛出的错误作为参数,并返回一个值更新state。
注意事项:它会在渲染阶段调用,因此不允许出现副作用【比如请求等】,如遇此类情况,请改用componentDidCatch
(2)componentDidCatch ------ 同上
他接受俩个参数
- error 抛出的错误
- info 带有componentStack key的对象,其中包含有关组件引发错误的栈信息。
它会在“组件提交”阶段被调用,因此允许执行副作用。它应该用作于记录错误之类的情况。
三、react生命周期执行的三个步骤【速查表】
四、强制刷新组件 – forceUpdate
默认情况下,当组件的state或props发生变化时,组件将重新渲染。如果render()方法依赖于其他数据,则可以调用forceUpdate()强制让组件重新渲染。
调用这个方法会跳过该组件的shouldComponentUpdate()。