生命周期是一个抽象的概念,在生命周期的整个过程,分成了很多个阶段:
比如挂载阶段(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.statereturn (<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.statereturn (<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.statereturn (<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.statereturn (<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
上文已有介绍