React - 生命周期
对 React 中 生命周期
的一些理解。
v16.0 前的生命周期
组件挂载: componentWillMount -> render -> componentDidMount
组件更新(state 变化): shoudlComponentUpdate -> componentWillUpdate -> render -> componentDidUpdate
组件更新(props 变化): componentWillReceiveProps -> shouldComponentUpdate -> componentWillUpdate -> render -> componentDidUpdate
组件销毁: componentWillUnmount
Initialization (初始化阶段)
constructor
触发时机:
子组件实例化时。
解析:
实际上将 constructor
作为生命周期钩子这一说法并不确切。constructor
作为 class(ES6) 中的 构造方法 ,通过 new
关键字创建对象实例时会自动调用,用于返回实例对象(this)。React 的生命周期定义在 Component
这个基类中,在执行 constructor()
后组件才会继承 Component
基类,才可以使用这些生命周期方法。所以 hooks
范式中没有生命周期,因为没有使用对象(返回函数组件)。
使用场景:
super(props)
用于调用父组件(基类)的构造方法,并将父组件的props
注入给子组件。在
this.state
中可以初始化state
内容。
|
Mounting (挂载阶段)
该阶段在组件第一次挂载时触发,只会执行一次。
componentWillMount()
触发时机:
组件挂在到 DOM 前 执行,只会执行一次。
解析:
不建议 在该阶段请求网络数据,因为一般网络请求都是以 异步 的方式进行,不能保证在 render()
前就完成,可能还没获取到数据就已经执行了渲染操作。至于初始化 state
,可以,但没必要,因为放到 constructor
更直观且主流。
使用场景:
不过 componentWillMount
也不是一无是处,在服务端渲染(SSR)中生命周期不全,componentWillMount
是唯一的生命周期钩子。
render()
第一次渲染。
componentDidMount()
触发时机:
组件挂在到 DOM 后 执行,只会触发一次。
解析:
在该阶段执行网络请求最合理,因为这时候已经完成渲染,有足够的时间去执行 异步请求,能够保证数据安全,且获取数据后一般会进行 setState()
操作,触发重渲染。
使用场景:
执行网络请求,获取远程数据。
使用
setState()
保存状态,触发重渲染。使用
ref
操作 DOM,因为这时候组件已经挂载到 DOM 上了。
|
Updation (更新阶段)
该阶段在组件 state
或 props
改变时触发,可能触发多次。
componentWillReceiveProps(nextProps)
触发时机:
组件挂载完成后,接收到新的 props
时触发,此时还没有触发重渲染。
解析:
可以通过 this.props
获取之前的属性,通过 nextProps
获取之后的属性,进行对比或其他操作。由于此时还没有触发重渲染,所以可以将一些 网络请求 放到这里执行,减轻负担,达到一定的优化效果。
使用场景:
对比
this.props
和nextProps
,执行后续操作。子组件发起 网络请求。
shouldComponentUpdate(nextProps, nextState)
触发时机:
组件挂载完成后,在接收到新的 state
或 props
时触发。
解析:
可以通过对比 this.state
和 nextState
,或 this.props
和 nextProps
是否发生变化,返回一个 boolean
值决定是否重渲染(返回 true
表示本次允许重渲染,返回 false
表示本次不允许重渲染),从而达到性能优化的目的。建议只进行浅比较(不递归比较嵌套对象),因为比较层级过多的话消耗掉会大于重渲染,得不偿失。在 React 15.3 后引入了 PureComponent
,代替了 shouldComponentUpdate
自动进行 浅比较 判断 state
或 props
是否更新,从而优化性能,不过它只对子组件生效。
使用场景:
判断 state
或 props
是否更新,从而决定是否渲染,达到性能优化的目的。
|
componentWillUpdate(nextProps, nextState)
触发时机:
state
或 props
发生变化时,重渲染 前 触发。
解析:
不能 在这个生命周期调用 setState()
,因为每次调用 setState()
触发重渲染都会经过这一生命周期,周而复始引起死循环。另外,若是 shouldComponentUpdate
返回了 fasle
阻止了重渲染,则不会触发 componentWillUpdate
。
使用场景:
可以做一些动画或 DOM 的初始化。
render()
重渲染。
componentDidUpdate(prevProps, prevState)
触发时机:
组件更新,重渲染 后 触发。
解析:
若是 shouldComponentUpdate
返回了 fasle
阻止了重渲染,也不会执行到这一步。
使用场景:
- 使用
ref
操作 DOM,因为这时候组件已经重渲染,且挂载到 DOM 上了。
解析:
Unmounting (卸载阶段)
该阶段在组件销毁时触发,只会触发一次。
componentWillUnmount()
触发时机:
在组件卸载之前执行。
解析:
由于是单页面应用(SPA),在路由跳转后,页面内的一些 事件订阅 或 定时操作 并不会主动销毁,任然留在内存中继续执行。此时会引发内存泄漏,影响性能;此外,由于组件已经销毁,this
已经解除链接,获取不到相关引用变量, 会造成 underfined
报错。
使用场景:
执行一些必要的清理操作,例如 clearInterval()
、 clearTimeout()
等。
|
v16.0 后的生命周期
在 React 16.0 之后,废除了 componentWillMount
、componentWillUpdate
、componentWillReceiveProps
这三个生命周期,将在 17.0 之后移除。因为引入了 fiber
架构,render()
前一阶段可以被打断并且执行多次,导致这些生命周期变得不可控。不过为了平滑升级,暂时可以使用 UNSAFE_componentWillMount
、UNSAFE_componentWillUpdate
、UNSAFE_componentWillReceiveProps
代替。
取而代之引入了两个新的生命周期:getDerivedStateFromProps
、getSnapshotBeforeUpdate
。
组件挂载: getDerivedStateFromProps -> render -> componentDidMount
组件更新: getDerivedStateFromProps -> shoudlComponentUpdate -> render -> getSnapshotBeforeUpdate -> componentDidUpdate
组件销毁: componentWillUnmount
static getDerivedStateFromProps(nextProps, prevState)
触发时机:
每次渲染前调用。(包括首次渲染和重渲染)
解析:
首先这是一个 static
方法,不能使用 this
访问对象属性,所以也就不能直接使用 this.setState()
方法了。该方法接受两个参数,nextProps
指接收的新属性,prevState
指当前状态,最后返回一个对象来更新当前 state
状态,如果不需要渲染则返回一个 null
。本质上就是讲传入的 props
映射到 state
。
使用场景:
替代之前的 componentWillReceiveProps
等生命周期。
|
getSnapshotBeforeUpdate(prevProps, prevState)
触发时机:
在渲染后,componentDidUpdate
前调用。
解析:
该方法接受两个参数,prevProps
指当前属性,prevState
指当前状态,最后返回一个对象传递给 componentDidUpdate
。
使用场景:
用于替代 componentWillUpdate
等生命周期。
参考资料
- 本文作者:zhaoo
- 本文链接:https://www.izhaoo.com/2020/05/17/react-lifecycle/index.html
- 版权声明:本博客所有文章均采用 BY-NC-SA 许可协议,转载请注明出处!