React Lifecyle
# 生命周期
# React16 之前的生命周期
需要注意的几点:
# 挂载
挂载过程在组件的一生中仅会发生一次,在这个过程中,组件被初始化,然后会被渲染到真实 DOM 里,完成所谓的“首次渲染”。
- constructor 方法,该方法仅仅在挂载的时候被调用一次,我们可以在该方法中对 this.state 进行初始化
- componentWillMount、componentDidMount 方法同样只会在挂载阶段被调用一次。其中 componentWillMount 会在执行 render 方法前被触发
- 接下来 render 方法被触发。注意 render 在执行过程中并不会去操作真实 DOM(也就是说不会渲染),它的职能是把需要渲染的内容返回出来。真实 DOM 的渲染工作,在挂载阶段是由 ReactDOM.render 来承接的
- componentDidMount 方法在渲染结束后被触发,此时因为真实 DOM 已经挂载到了页面上,我们可以在这个生命周期里执行真实 DOM 相关的操作。此外,类似于异步请求、数据初始化这样的操作也大可以放在这个生命周期来做
# 更新
- 更新过程:更新分为两种,一种是由父组件更新触发的更新,另一种是组件自身调用自己的 setState 触发的更新。
componentWillReceiveProps
并不是由 props 的变化触发的,而是由父组件的更新触发的。只要父组件重新渲染,不管传递给子组件的 props 的值有没有发生变化,子组件都会触发componentWillReceiveProps
这个生命周期。shouldComponentUpdate(nextProps, nextState)
- render 方法由于伴随着对虚拟 DOM 的构建和对比,过程可以说相当耗时。而在 React 当中,很多时候我们会不经意间就频繁地调用了 render。为了避免不必要的 render 操作带来的性能开销,React 为我们提供了 shouldComponentUpdate 这个钩子。
- React 组件会根据 shouldComponentUpdate 的返回值,来决定是否执行该方法之后的生命周期,进而决定是否对组件进行 re-render(重渲染)。shouldComponentUpdate 的默认值为 true,也就是说“无条件 re-render”。在实际的开发中,我们往往通过手动往 shouldComponentUpdate 中填充判定逻辑,或者直接在项目中引入
PureComponent
等最佳实践,来实现“有条件的 re-render”。
# 卸载
- 卸载过程:组件销毁的常见原因有以下两个。
- 组件在父组件中被移除了:这种情况相对比较直观
- 组件中设置了 key 属性,父组件在 render 的过程中,发现 key 值和上一次不一致,那么这个组件就会被干掉
# React16 之后的生命周期
从 v16.3
的版本开始, 对生命周期的钩子进行了调整,分别废弃和新增了一些生命周期的钩子函数。React 16 对 render 方法也进行了一些改进:React 16 之前,render 方法必须返回单个元素,而 React 16 允许我们返回元素数组和字符串
从以上生命周期的对比,我们不难看出,React 从 v16.3 开始废弃
componentWillMount、componentWillReceiveProps、componentWillUpdate
三个钩子函数
# 挂载
消失的
componentWillMount
,新增的static getDerivedStateFromProps
getDerivedStateFromProps
不是 componentWillMount 的替代品- getDerivedStateFromProps 这个 API,其设计的初衷不是试图替换掉 componentWillMount,而是试图替换
componentWillReceiveProps
,因此它有且仅有一个用途:使用 props 来派生/更新 state - React 团队为了确保 getDerivedStateFromProps 这个生命周期的纯洁性,直接从命名层面约束了它的用途(getDerivedStateFromProps 直译过来就是**“从 Props 里派生 State”**)。所以,如果你不是出于这个目的来使用 getDerivedStateFromProps,原则上来说都是不符合规范的
- getDerivedStateFromProps 这个 API,其设计的初衷不是试图替换掉 componentWillMount,而是试图替换
# static getDerivedStateFromProps(props, state)
- getDerivedStateFromProps 是一个静态方法。静态方法不依赖组件实例而存在,因此你在这个方法内部是访问不到 this 的。
- 该方法可以接收两个参数:props 和 state,它们分别代表当前组件接收到的来自父组件的 props 和 组件自身的 state
- getDerivedStateFromProps 需要一个对象格式的返回值,React 需要用这个返回值来更新(派生)组件的 state。getDerivedStateFromProps 方法对 state 的更新动作并非“覆盖”式的更新,而是针对某个属性的定向更新
# 更新
消失的
componentWillReceiveProps
,新增的static getDerivedStateFromProps(props, state)
消失的
componentWillUpdate
与新增的getSnapshotBeforeUpdate(prevProps, prevState)
# 卸载
# 生命周期变更原因
因为在 v15 的版本,更新过程是同步的,往往一个主线程长时间被占用,会导致页面性能问题。React16 以后采用了新的 Fiber 机制:利用浏览器 requestIdleCallback
将可中断的任务进行分片处理,每一个小片的运行时间很短,这样主线程就不会被独占
由于一次更新过程会分成多个分片完成,所以完全有可能一个更新任务还没有完成,就被另一个更高优先级的更新过程打断,这时候,优先级高的更新任务会优先处理完,而低优先级更新任务所做的工作则会完全作废,然后等待机会重头再来。
因为一个更新过程可能被打断,所以 React Fiber 一个更新过程被分为两个阶段(Phase):
- 第一个阶段
Reconciliation(调和)
,React Fiber 会找出需要更新哪些 DOM,这个阶段是纯净且没有副作用,可能会被 React 暂停、终止或重新启动。 - 第二阶段
Commit
,一鼓作气把 DOM 更新完,绝不会被打断。而 commit 阶段又被细分为了- pre-commit:可以读取 DOM
- commit:可以使用 DOM,运行副作用,安排更新
# constructor
constructor() 在 React 组件挂载之前被调用,在为 React.Component 子类实现构造函数时,应在其他语句之前调用 super()