所谓生命周期就是代码被读取到的那一刻到页面渲染完成的历程
react 的生命周期相比较 vue 会显得复杂点
钩子链接: https://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/
整个过程被分成了三部分:挂载时
、更新时
、卸载时
1.挂载时
挂载时有个 constructor
,这不是类中的构造器吗,然后 render
不是类编程中的函数吗,下面简单写一个类组件
import React, { Component } from 'react'
export default class Life extends Component {
constructor () {
super()
console.log('组件开始加载');
this.name = 'Dolphin'
}
componentDidMount() {
console.log('组件挂载完成');
}
render() {
console.log('组件开始被编译');
return (
<div>
{this.name}
</div>
)
}
}
挂载时:先执行 constructor
,此时就是获得数据源,再执行 render
渲染,编译得到虚拟 dom
,中间穿插了个更新 DOM
和 refs
,最后执行 componentDidMount
,这个 did
就是过去时,因此就是 vue 中的 onMounted
,挂载完成。
更新 dom 不是生命周期,只是这个过程在这里发生
render
的目的是生成虚拟 dom,这个时候可以拿到 dom,但是值是空的,下面展示下,先引入 createRef
函数拿到 h4
的 dom,如下
import React, { Component, createRef } from 'react'
export default class Life extends Component {
constructor () {
super()
console.log('组件开始加载', this.ref);
this.name = 'Dolphin'
this.ref = createRef() // 存放dom
}
componentDidMount() {
console.log('组件挂载完成', this.ref);
}
render() {
console.log('组件开始被编译', this.ref);
return (
<div>
<h4 ref={this.ref}>{this.name}</h4>
</div>
)
}
}
这就有点像是 vue 中的 beforeMount
,均拿不到 dom
因此接口请求我们一般写在挂载完成 componentDidMount
中,因为有时候接口请求速度很快,会出现数据拿到了,但是 dom 还没渲染完成,若接口请求还想要操作 dom,就会失败,为了更好的操作 dom,请求我们写在挂载完成后
2. 更新时
更新阶段的 New props
就是拿到父组件传递过来的值,setState
是自身数据源的改变,forceUpdate
也是数据更新的方法,这是强制更新,这三个都会再一次触发 render
,渲染完后又是更新 dom,最后又是更新 componentDidUpdate
。
因此更新阶段的钩子就只有两个,一个 render
,一个 componentDidUpdate
刚刚上面的我改下,添加一个 setState
,就是响应式更改数据源,那就一定会重新触发 render
和 componentDidUpdate
import React, { Component, createRef } from 'react'
export default class Life extends Component {
constructor () {
super()
this.ref = createRef() // 存放dom
this.state = {
count: 1
}
}
handleClick = () => {
this.setState({
count: this.state.count + 1
})
}
componentDidUpdate() {
console.log('组件更新完成');
}
render() {
console.log('组件开始被编译', this.ref);
return (
<div>
<h4 ref={this.ref} onClick={() => this.handleClick()}>{this.state.count}</h4>
</div>
)
}
}
其实 react 中并没有说响应式这个概念,他就是原生 js,
setState
是在数据更新时劫持了这个数据,setState
可以帮我们触发render
,更新视图,若不用setState
,数据是能照常更改,只是视图没有更新罢了
触发 render
操作的还有个 forceUpdate
,这是强制更新,我们可以不用 setState
来改,如下
import React, { Component, createRef } from 'react'
export default class Life extends Component {
constructor () {
super()
this.ref = createRef()
this.count = 1
}
handleClick = () => {
this.count++
this.forceUpdate() // 强制render重新调用
}
componentDidUpdate() {
console.log('组件更新完成');
}
render() {
console.log('组件开始渲染', this.ref);
return (
<div>
<h4 ref={this.ref} onClick={() => this.handleClick()}>{this.count}</h4>
</div>
)
}
}
在某些场景下,我们需要用 forceUpdate
来进行强制更新的
组件更新完成在 react 中只有一个 componentDidUpdate
,而 vue 中就是 beforeUpdate
和 updated
,其实 react 中的 render
就是充当了 beforeUpdate
3.卸载时
就一个钩子 componentWillUnmount
,组件卸载时执行
componentWillUnmount () {
console.log('组件即将卸载');
}
4.总结
挂载时
constructor
:类中的构造器充当了生命周期,此时拿到数据源render
:相当于vue中的 beforeMount,还拿不到 domcomponentDidMount
:挂载完成,可以拿到 dom
更新时
render
:父组件传参 New props,修改数据源 setState,强制更新 forceUpdated 都会重新渲染 rendercomponentDidUpdate
:更新完成
卸载时
componentWillUnmount
:组件即将卸载时执行
5. 不常用的生命周期函数
常用的就是上面五个生命周期函数 constructor
、render
、componentDidMount
、componentDidUpdate
和 componentWillUnmount
,不常用的还有,如图,我把不常用的也展开来
static getDerivedStateFromProps()
这是个静态方法,因此这个生命周期函数只能在类组件中使用,这个方法比较冷门,它在render前调用,自行查看罕见用例
shouldComponentUpdate()
这个生命周期函数表示组件该不该更新,若里面返回 false
,就不会给你更新视图,但是数据照常更新
import React, { Component, createRef } from 'react'
export default class Life extends Component {
constructor () {
console.log('组件开始加载');
super()
this.ref = createRef() // 存放dom
this.state = {
count: 1
}
}
handleClick = () => {
this.setState({
count: this.state.count + 1
})
console.log(this.state.count);
}
shouldComponentUpdate() {
return false
}
render() {
console.log('组件开始渲染', this.ref);
return (
<div>
<h4 ref={this.ref} onClick={() => this.handleClick()}>{this.state.count}</h4>
</div>
)
}
}
打印下 count
的值,发现确实变化了,但是视图没有更新
getSnapshotBeforeUpdate()
这个函数返回的值会给到 componentDidUpdate
,它在最近一个渲染输出后调用,也就是 render
完之后立马调用
这么看 react 的生命周期函数总共也就是 8 个,和 vue 的生命钩子数量差不多
参考文章:https://juejin.cn/post/7359821247675596835