生命周期是一个抽象的概念,在生命周期的整个过程,分成了很多个阶段:
比如挂载阶段(Mount),组件第一次在DOM树中被渲染的过程;
比如更新过程(Update),组件状态发生变化,重新更新渲染的过程;
比如卸载过程(Unmount),组件从DOM树中被移除的过程;
React内部为了告诉我们当前处于哪些阶段,会对我们组件内部实现的某些函数进行回调,这些函数就是生命周期函数:
- 比如实现
componentDidMount
函数:组件已经挂载到DOM上时,就会回调; - 比如实现
componentDidUpdate
函数:组件已经发生了更新时,就会回调; - 比如实现
componentWillUnmount
函数:组件即将被移除时,就会回调;
因此我们可以在这些回调函数中编写自己的逻辑代码,来完成自己的需求功能;
1、常用生命周期
1.1 挂载阶段
1、在挂载阶段,即创建组件实例的时候会先执行组件的constructor
方法
- 如果不初始化 state 或不进行方法绑定,则不需要为 React 组件实现构造函数
constructor中通常只做两件事情:
1、通过给 this.state 赋值对象来初始化内部的state
2、为事件绑定实例(this)
2、紧接着会执行组件的render
方法
3、最后会执行 componentDidMount
方法, 该方法会在组件挂载后(插入 DOM 树中)立即调用
- 依赖于DOM的操作可以在这里进行
- 此处是发送网络请求最好的地方(官方建议)
- 可以在此处添加一些订阅(会在componentWillUnmount取消订阅)
Hello.jsx
import React from 'react';
export default class Hello extends React.Component{
constructor() {
super()
console.log('hello constructor')
}
render() {
console.log('hello render')
return (
<h1>hello</h1>
)
}
}
App.jsx
import React from 'react'
import Hello from './Hello'
export default class App extends React.Component {
render() {
return (
<div>
<Hello />
<Hello />
</div>
)
}
}
可见如果使用多次组件实例,该组件每次挂载时都会先执行constructor
方法再执行render
方法
如果在class组件中定义了componentDidMount
函数,那么当组件挂载完毕后会被回调:
import React from 'react';
export default class Hello extends React.Component{
constructor() {
super()
console.log('hello constructor')
}
render() {
console.log('hello render')
return (
<h1>hello</h1>
)
}
componentDidMount() {
console.log('componentDidMount...')
}
}
1.2 更新阶段
一旦执行了this.setState
方法就会触发组件的更新:
- 此时会立即执行组件的
render
方法 - 在组件更新完后
componentDidUpdate
会被立即调用
import React from 'react';
export default class Hello extends React.Component{
constructor() {
super()
this.state = {
message: 'hello world'
}
console.log('hello constructor')
}
changeText() {
this.setState({ message: 'hello react'})
}
render() {
console.log('hello render')
const {message} = this.state
return (
<div>
<h1>{message}</h1>
<button onClick={() => this.changeText()}>修改</button>
</div>
)
}
componentDidMount() {
console.log('componentDidMount...')
}
componentDidUpdate() {
console.log('componentDidUpdate...')
}
}
1.3 卸载阶段
componentWillUnmount
方法会在组件卸载及销毁之前直接调用 :
- 在此方法中执行必要的清理操作
- 例如,清除 timer,取消网络请求或清除在
componentDidMount()
中创建的订阅等
Hello.jsx
import React from 'react';
export default class Hello extends React.Component{
constructor() {
super()
this.state = {
message: 'hello world'
}
console.log('hello constructor')
}
render() {
console.log('hello render')
const {message} = this.state
return (
<div>
<h1>{message}</h1>
</div>
)
}
componentDidMount() {
console.log('componentDidMount...')
}
componentWillUnmount() {
console.log('componentWillUnmount...')
}
}
App.jsx
import React from 'react'
import Hello from './Hello'
export default class App extends React.Component {
constructor() {
super()
this.state = {
isShow: true
}
}
changeShow() {
this.setState({ isShow: !this.state.isShow})
}
render() {
return (
<div>
<button onClick={() => this.changeShow()}>切换</button>
{ this.state.isShow && <Hello />}
</div>
)
}
}
2、不常用生命周期
2.1 挂载阶段
在挂载阶段,执行完毕constructor
后还会执行 static getDerivedStateFromProps()
方法,执行顺序依次为:
- constructor()
- static getDerivedStateFromProps()
- render()
- componentDidMount()
static getDerivedStateFromProps(props, state)
该方法使用场景比较罕见,如果state的值在任何时候都依赖于props时才使用此方法
- 该方法会在render前被调用,并且在初始挂载及后续更新时都会被调用
- 该方法会返回一个对象来更新state,如果返回null则不更新任何内容
- 该方法的存在只有一个目的:让组件在props变化时来更新state
2.2 更新阶段
在更新阶段会依次执行:
static getDerivedStateFromProps()
shouldComponentUpdate()
render()
getSnapshotBeforeUpdate()
componentDidUpdate()
shouldComponentUpdate(nextProps, nextState)
当props或state发生变化时,该方法会在渲染之前被调用,返回值默认是true,如果返回false则不重新渲染组件。
- 该方法常用于性能优化,不要企图依靠此方法来阻止渲染,因为这可能会产生bug
- 如果返回了false则不会执行
render
、componentDidUpdate
App.jsx
import React from "react"
import HelloWorld from "./HelloWorld"
class App extends React.Component {
render() {
return (
<div>
<HelloWorld/>
</div>
)
}
}
export default App
HelloWord.jsx
import React from "react"
class HelloWorld extends React.Component {
constructor() {
console.log("constructor...")
super()
this.state = {
message: "Hello World"
}
}
changeText() {
this.setState({ message: "Hello React" })
}
render() {
console.log("render...")
const { message } = this.state
return (
<div>
<h2>{message}</h2>
<button onClick={e => this.changeText()}>修改文本</button>
</div>
)
}
componentDidMount() {
console.log("componentDidMount...")
}
componentDidUpdate() {
console.log("componentDidUpdate...")
}
shouldComponentUpdate() {
return true
}
}
export default HelloWorld
如果 shouldComponentUpdate()
返回了true,在点击按钮修改文本时:
如果 shouldComponentUpdate()
返回了false,在点击按钮修改文本时:
shouldComponentUpdate() {
return false
}
getSnapshotBeforeUpdate(prevProps, prevState)
该方法会在componentDidUpdate
之前被调用,可以用来在组件发生变更前从DOM中捕获一些信息(如滚动位置)。
- 该方法返回的内容会作为参数传递给
componentDidUpdate
import React from "react"
class HelloWorld extends React.Component {
constructor() {
console.log("constructor...")
super()
this.state = {
message: "Hello World"
}
}
changeText() {
this.setState({ message: "Hello React" })
}
render() {
console.log("render...")
const { message } = this.state
return (
<div>
<h2>{message}</h2>
<button onClick={e => this.changeText()}>修改文本</button>
</div>
)
}
componentDidMount() {
console.log("componentDidMount...")
}
componentDidUpdate(prevProps, prevState, snapshot) {
console.log("componentDidUpdate...", prevProps, prevState, snapshot)
}
getSnapshotBeforeUpdate() {
console.log('getSnapshotBeforeUpdate...')
return {
scrollTop: 100
}
}
}
export default HelloWorld
3、对比老版生命周期
3.1 react生命周期(旧)
3.2 react生命周期(新)
3.3 对比
通过两个图的对比,可以发现新版的生命周期减少了以下三种方法:
componentWillMount
componentWillReceiveProps
componentWillUpdate
其实这三个方法仍然存在,只是在前者加上了UNSAFE_
前缀,如UNSAFE_componentWillMount
,并不像字面意思那样表示不安全,而是表示这些生命周期的代码可能在未来的 react版本可能废除
同时也新增了两个生命周期函数:
getDerivedStateFromProps
getSnapshotBeforeUpdate
上文已有介绍