React高级进阶教程-为什么React16要更改组件的生命周期?

124 阅读5分钟

React 生命周期

入门教材在设计上往往追求的是“简单省事、迅速上手”

“背就完了、别想太多”

必须要求自己在知其然的基础上,知其所以然

引言

对于 React15、React16 两个版本的生命周期进行探讨、比对和总结

通过搞清楚一个又一个的“Why”

来帮你建立系统而完善的生命周期知识体系

生命周期背后的设计思想:把握 React 中的“大方向”

初步理解 React 框架中的一些关键的设计思想

“组件”和“虚拟DOM”这两个词的出镜率是非常高的

它们是 React 基本原理中极为重要的两个概念

也是我们这个小节的学习切入点

虚拟 DOM: 核心算法的基石

组件初始化

-- render 方法 -> 生成虚拟 DOM

-- ReactDOM.render 方法 -> 真实 DOM

组件更新

-- render 方法 -> 生成新的虚拟 DOM

-- diff 算法 -> 定位出两次虚拟 DOM 的差异

组件化:工程化思想在框架中的落地

组件化是 React 团队在研发效能方面所做的一个重要的努力

几乎所有的可见/不可见的内容都可以被抽离为各种各样的组件,每个组件既是“封闭”的,也是“开放”的

封闭:针对“渲染工作流”来说的

在组件自身的渲染工作流中,每个组件都只处理它内部的渲染逻辑

开放:针对“组件间通信”来说的

React 允许开发者基于“单向数据流”的原则,完成组件间的通信,而组件之间的通信又将改变通信双方/某一方内部的数据,进而对渲染结果造成影响

生命周期方法的本质:组件的“灵魂”与“躯干”

渲染工作流:指的是从组件数据改变到组件实际更新发生的过程

render 方法是组件的灵魂,render 之外的生命周期方法可以理解为组件的“躯干”。

“躯干”未必总是会做具体的事情

倘若“躯干”做了点什么

往往都会直接或间接地影响到“灵魂”

拆解 React 生命周期:从 React 15 说起

认识 React 15 的生命周期流程

constructor()
componentWillReceiveProps()
componentWillMount()
componentWillUpdate()
componentDidUpdate()
componentDidMount()
render()
componentWillUnmount()

image.png

Updating 阶段:组件的更新

如果父组件导致组件重新渲染,即使 props 没有更改,也会调用此方法。如果只想处理更改,请确保进行当前值与变更值的比较。

componentReceiveProps 并不是由 props 的变化触发的,而是由父组件的更新触发的。

这里只需要了解 shouldComponentUpdate 的基本使用即可。

Unmounting 阶段:组件的卸载

  • 组件在父组件中被移除了
  • 组件中设置了 key 属性
    • 父组件在 render 的过程中,发现 key 值和上一次不一致

进化的生命周期方法:React 16 生命周期工作流详解

image.png

image.png

getDerivedStateFromProps 不是 componentWillMount 的替代品

componentWillMount 的存在不仅“鸡肋”而且危险,因为它并不值得被“代替”,它就应该被废弃。

getDerivedStateFromProps 有且仅有一个用途:使用 props 来派生/更新 state

React 团队直接从命名层面约束了它的用途

  • 是一个静态方法
  • 该方法可以接收两个参数:props和state
  • 需要一个对象格式的返回值

getDerivedStateFromProps 方法对 state 的更新动作并非“覆盖”式的更新,而是针对某个属性的定向更新。

image.png

为什么使用 getDerivedStateFromProps 代替 componentWillReceiveProps?

  • getDerivedStateFromProps 是作为一个试图替代 componentWillReceiveProps 而出现的
  • 但两者不能直接画等号

消失的 componentWillUpdate 与新增的 getSnapshotBeforUpdate

getSnapshotBeforUpdate 的返回值会作为第三个参数给到 componentDidUpdate,它的执行时机是在 render 方法之后,真实 DOM 更新之前

同时获取到更新前的真实 DOM 和更新前后的 state&props 信息

实现一个内容会发生变化的滚动列表
要求根据滚动列表的内容是否发生变化
来决定是否要记录滚动条的当前位置

透过现在看本质:React 16 缘何两次求变?

Fiber 是 React 16 对 React 核心算法的一次重写
Fiber 会使原本同步的渲染过程变成异步的

image.png

Fiber 会将一个大一更新任务拆解为许多个小任务。

image.png

换个角度看生命周期工作流

Fiber 架构的重要特征就是可以被打断的异步渲染模式

根据“能否被打断”这一标准

React 16的生命周期被划分为了 render 和 commit 两个阶段

细说生命周期“废旧立新”背后的考虑

render 阶段是允许暂停、终止和重启的

这就层致 render 阶段的生命周期都是有可能被重复执行的

image.png

  1. 完全可以转移到其他生命周期里去做(尤其是 componentDidxxx)
  2. 在 Fiber 带来的异步渲染机制下,可能会导致非常严重的 bug

在 render 阶段里面的生命周期都可以重复执行,在 componentWillxxx 被打断+重启多次后,就会发出多个付款请求

  1. 即使你没有开启异步,React 15 下也有不少人能把自己“玩死”,比如 setState 搞的死循环

React 16 改造生命周期的主要动机

是为了配合 Fiber 架构带来的异步渲染机制

总结

现有的生命周期,虽然已经对方法的最佳实践做了强约束,但是仍然无法覆盖所有的“误操作”。

# 你可能不需要使用派生 state