React16 ~ React19 类组件完整生命周期(挂载 / 更新 / 卸载)
React 类组件生命周期在 React16 之后变化非常大:
-
React15:旧生命周期
-
React16:引入 Fiber 架构
-
React16.3:新增安全生命周期
-
React17:基本延续 React16
-
React18:并发特性开始影响生命周期调用
-
React19:类组件仍支持,但官方主推函数组件 Hooks
下面我按:
-
生命周期总览
-
挂载阶段
-
更新阶段
-
卸载阶段
-
错误处理阶段
-
React16~19变化对比
-
执行顺序图
-
面试高频点
完整讲透。
一、React 类组件生命周期总览
React16+ 生命周期:
挂载 Mounting
constructor
static getDerivedStateFromProps
render
componentDidMount
更新 Updating
static getDerivedStateFromProps
shouldComponentUpdate
render
getSnapshotBeforeUpdate
componentDidUpdate
卸载 Unmounting
componentWillUnmount
错误处理 Error Handling
static getDerivedStateFromError
componentDidCatch
二、React16 之前旧生命周期(了解)
React16.3 之前:
componentWillMount
componentWillReceiveProps
componentWillUpdate
后来因为:
-
异步渲染不安全
-
容易造成副作用
-
Concurrent Mode 会重复执行
所以被废弃。
React16.3 开始:
UNSAFE_componentWillMount
UNSAFE_componentWillReceiveProps
UNSAFE_componentWillUpdate
React17+ 不推荐使用。
React19 依然还能用,但属于遗留 API。
三、挂载阶段(Mounting)
组件第一次进入页面。
1.constructor
组件初始化。
class Demo extends React.Component {
constructor(props) {
super(props)
this.state = {
count: 0
}
}
}
作用:
-
初始化 state
-
绑定 this
-
创建 ref
注意:
❌ 不要:
this.setState()
因为组件还没挂载。
执行顺序:
new 组件实例
↓
constructor
2.static getDerivedStateFromProps
React16.3 新增。
用于:
-
根据 props 派生 state
static getDerivedStateFromProps(props, state) {
if (props.count !== state.count) {
return {
count: props.count
}
}return null}
特点:
-
静态方法
-
没有 this
-
必须 return object/null
执行时机:
初始化
+
props更新
都会执行
注意:
这是:
render前
最后一次修改state机会
不推荐滥用。
很多场景:
props -> 直接使用
不需要同步到 state。
3.render
必须实现。
render() {
return <div>Hello</div>
}
作用:
-
返回 JSX
-
生成 Virtual DOM
特点:
必须纯函数:
❌ 不允许:
ajax
setTimeout
setState
操作DOM
因为 render 可能重复执行。
4.componentDidMount
组件挂载完成。
componentDidMount() {
console.log('mounted')
}
执行时机:
真实DOM完成后
这里最适合:
✅ 请求接口
axios.get()
✅ 操作 DOM
this.ref.current.focus()
✅ 开启定时器
setInterval()
✅ 初始化图表
echarts.init()
完整挂载顺序:
constructor
↓
getDerivedStateFromProps
↓
render
↓
componentDidMount
四、更新阶段(Updating)
触发更新:
1. props变化
2. setState
3. forceUpdate
更新生命周期总流程
getDerivedStateFromProps
↓
shouldComponentUpdate
↓
render
↓
getSnapshotBeforeUpdate
↓
componentDidUpdate
1.getDerivedStateFromProps
更新时也会执行。
static getDerivedStateFromProps(props, state)
作用:
根据新的 props 更新 state。
2.shouldComponentUpdate
性能优化核心。
shouldComponentUpdate(nextProps, nextState) {
return true
}
返回值:
true -> 继续更新
false -> 阻止更新
优化例子:
shouldComponentUpdate(nextProps, nextState) {
return nextState.count !== this.state.count
}
React 提供:
PureComponent
自动浅比较。
等价于:
shouldComponentUpdate + shallowCompare
3.render
重新生成 Virtual DOM。
4.getSnapshotBeforeUpdate
React16.3 新增。
在:
DOM更新前
获取 DOM 快照。
getSnapshotBeforeUpdate(prevProps, prevState) {
return this.container.scrollHeight
}
返回值会传给:
componentDidUpdate
典型场景:
聊天窗口滚动位置。
执行时机:
render之后
真实DOM更新之前
5.componentDidUpdate
更新完成。
componentDidUpdate(prevProps, prevState, snapshot) {
}
参数:
prevProps
prevState
snapshot
适合:
✅ ajax重新请求
✅ DOM操作
✅ 第三方库更新
注意:
必须加条件。
❌
componentDidUpdate() {
this.setState({})
}
会无限循环。
正确:
componentDidUpdate(prevProps) {
if (prevProps.id !== this.props.id) {
this.setState({})
}
}
五、卸载阶段(Unmounting)
componentWillUnmount
组件销毁前。
componentWillUnmount() {
clearInterval(this.timer)
}
作用:
✅ 清除定时器
✅ 取消订阅
✅ 移除事件监听
✅ 销毁实例
典型:
window.removeEventListener()
echarts.dispose()
注意:
组件即将销毁:
❌ 不要:
this.setState()
六、错误处理生命周期(Error Boundaries)
React16 新增。
1.static getDerivedStateFromError
发生错误时更新 UI。
static getDerivedStateFromError(error) {
return {
hasError: true
}
}
2.componentDidCatch
捕获错误日志。
componentDidCatch(error, info) {
console.log(error)
}
典型:
错误监控
Sentry
日志平台
七、React16~19 生命周期变化
React16
重大变化:
✅ Fiber架构
新增:
getDerivedStateFromProps
getSnapshotBeforeUpdate
componentDidCatch
废弃:
componentWillMount
componentWillReceiveProps
componentWillUpdate
React17
基本无生命周期变化。
主要:
为React18铺路
React18
并发渲染开始影响生命周期。
开发模式 StrictMode:
某些生命周期会执行两次。
例如:
constructor
render
componentDidMount
原因:
检测副作用。
不是 bug。
例如:
<React.StrictMode>
<App />
</React.StrictMode>
开发环境可能:
mount
unmount
再mount
React19
React19:
-
类组件仍支持
-
生命周期仍可用
-
官方全面推荐 Hooks
未来趋势:
类组件 -> 维护旧项目
函数组件 -> 主流
八、完整生命周期执行顺序图
首次挂载
constructor
↓
getDerivedStateFromProps
↓
render
↓
componentDidMount
更新阶段
getDerivedStateFromProps
↓
shouldComponentUpdate
↓
render
↓
getSnapshotBeforeUpdate
↓
componentDidUpdate
卸载阶段
componentWillUnmount
错误阶段
getDerivedStateFromError
↓
componentDidCatch
九、废弃生命周期执行顺序(旧项目)
旧版:
componentWillMount
render
componentDidMount
更新:
componentWillReceiveProps
shouldComponentUpdate
componentWillUpdate
render
componentDidUpdate
React16.3后:
UNSAFE_componentWillMount
UNSAFE_componentWillReceiveProps
UNSAFE_componentWillUpdate
十、完整生命周期代码示例
class Demo extends React.Component {
constructor(props) {
super(props)
this.state = {
count: 0
}
console.log('constructor')
}
static getDerivedStateFromProps(props, state) {
console.log('getDerivedStateFromProps')
return null
}
shouldComponentUpdate(nextProps, nextState) {
console.log('shouldComponentUpdate')
return true
}
render() {
console.log('render')
return (
<div>
{this.state.count}
<button
onClick={() => {
this.setState({
count: this.state.count + 1
})
}}
>
+
</button>
</div>
)
}
componentDidMount() {
console.log('componentDidMount')
}
getSnapshotBeforeUpdate(prevProps, prevState) {
console.log('getSnapshotBeforeUpdate')
return 'snapshot'
}
componentDidUpdate(prevProps, prevState, snapshot) {
console.log('componentDidUpdate', snapshot)
}
componentWillUnmount() {
console.log('componentWillUnmount')
}
static getDerivedStateFromError(error) {
return {
hasError: true
}
}
componentDidCatch(error, info) {
console.log(error, info)
}
}
十一、面试高频问题
1.render 能 setState 吗?
不能。
会死循环。
2.componentDidMount 为什么适合请求接口?
因为:
DOM已存在
不会阻塞首次渲染
3.shouldComponentUpdate 有什么用?
性能优化。
减少无意义 render。
4.getSnapshotBeforeUpdate 用在哪?
获取 DOM 更新前状态。
典型:
滚动条位置
5.为什么废弃 componentWillXXX?
因为:
异步渲染不安全
可能重复执行
6.React18 为什么生命周期执行两次?
StrictMode 检测副作用。
仅开发环境。
十二、React16~19 生命周期脑图(简版)
挂载
constructor
↓
getDerivedStateFromProps
↓
render
↓
componentDidMount
更新
getDerivedStateFromProps
↓
shouldComponentUpdate
↓
render
↓
getSnapshotBeforeUpdate
↓
componentDidUpdate
卸载
componentWillUnmount
错误
getDerivedStateFromError
↓
componentDidCatch