前言
在学习了虚拟 DOM 后,可以了解到虚拟->真实的转变过程其实就是:生命周期 render()生成虚拟 DOM,然后丢给 ReactDOM.render()挂在到真实 dom 上。
在初始化和更新时,生命周期的 render 的操作对象都是虚拟 dom
React 15 的生命周期
-
挂载
- constructor()
- componentWillMount()
- render()
- componentDidMount()
-
更新
- componentWillReceiveProps(nextProps)
- shouldComponentUpdate(nextProps,nextState)
- componentWillUpdate()
- render()
- componentDidUpdate(prevProps,prevState)
注意: componentWillReceiveProps() 触发的时机并不是 props 改变,而且父组件的发生更新,即使 props 未改变一样会触发
-
卸载
- componentWillUnmount()
触发时机:1. 组件被移除; 2. 组件中设置了 key 属性,render 中发现 key 不一致,直接删掉该组件
- componentWillUnmount()
React 16.4 之后的生命周期
-
挂载
- constructor()
- static getDerivedStateFromProps(props,state)
- render()
- componentDidMount()
-
更新
- static getDerivedStateFromProps(props,state)
- shouldComponetUpdae()
- render()
- getSnapshotBeforeUpdate(prevProps,prevState)
- componentDidUpdate(prevProps,prevState,snapshot)
-
卸载
- componentWillUnmount()
16.4 之后 newProps 和 setState()以及 forceUpdate()都会触发这个静态方法 getDerivedStateFromProps.getDerivedStateFromProps 严格上并不是用来代替之前的生命周期,顾名思义,它更加专注,只是用来从 props 派生出 state。
始作俑者
促使 React 生命周期发生如此变化的主要原因就是 Fiber 的出现。react15 的每次更新都是对两棵虚拟 DOM 树进行 diff,然后进行定向更新。不难想到对于复杂的组件来说,会有一个相当深的层级嵌套,如果修改最上层的状态,就会有一个很长的调用栈,这一个递归的过程,只有最底层的调用栈返回了,整个渲染过程才会逐层返回。同步渲染过程一旦开始就会霸占住主线程,直到递归渲染完成,这期间除了渲染什么都干不了,无法处理用户交互
。所以一旦时间过长就会有卡顿或者卡死的风险。
Fiber 应运而生,会将一个大的更新任务拆解为小任务,每次完成任务都会把主线程交回去,看看有没有更重要的工作需要处理。
Fiber 本质上是一个虚拟的堆栈帧,新的调度器会按照优先级自由调度这些帧,从而将之前的同步渲染改成了异步渲染,在不影响体验的情况下去分段计算更新
异步渲染的两个阶段
- reconciliation 可打断
- commit 不可打断
从生命周期看分为三个阶段(见生命周期图谱更直观)
- render 阶段
- pre-commit 阶段
- commit 阶段
render 阶段在执行过程中允许被打断,而 commit 阶段则总是同步执行的
被废弃的生命周期都是在 render 阶段的,也就是可以打断的,所以会导致废弃的生命周期重复执行,产生 bug。