blog icon indicating copy to clipboard operation
blog copied to clipboard

React进阶系列之生命周期

Open campcc opened this issue 4 years ago • 0 comments

React 生命周期已经是一个老生常谈的话题了,几乎没有哪一门 React 入门教材会省略对组件生命周期的介绍。然而,入门教材在设计上往往追求的是“简单省事、迅速上手”,这就导致许多同学对于生命周期知识的刻板印象为“背就完了、别想太多”。

“背就完了”这样简单粗暴的学习方式,或许可以帮助你理解“What to do”,到达“How to do”,但却不能帮助你去思考和认知“Why to do”。作为一个专业的 React 开发者,我们必须要求自己在知其然的基础上,知其所以然。

以史为镜,可以知兴衰,React 生命周期经历了 V15, V16.3, V16.4 等多个版本的迭代,我们有必要对这版本的生命周期进行探讨、比对和总结,通过搞清楚一个又一个的“Why”,来建立系统而完善的生命周期知识体系

生命周期背后的设计思想有哪些?

组件化和虚拟 DOM

虚拟 DOM 在整个 React 工作流中有哪些作用?

虚拟 DOM 在 React 工作流的两个阶段(挂载和更新)中都是核心算法的基石。组件在初始化时,会通过调用生命周期中的 render 方法,生成虚拟 DOM,然后再通过调用 ReactDOM.render 实现虚拟 DOM 到真实 DOM 的转换;当组件更新时,会再次通过调用 render 方法生成新的虚拟 DOM,然后借助 diff 定位出两次虚拟 DOM 的差异,从而针对发生变化的真实 DOM 做定向更新。

组件化作为一种优秀的设计思想,在 React 中有哪些体现?

React 组件遵循“封闭开放”原则。所谓封闭,主要是针对渲染工作流,也就是从组件数据改变到更新发生的过程,组件拥有自身的渲染工作流,每个组件只处理其内部的渲染逻辑,在没有数据流交互的情况下,组件与组件之间可以做到各自为政;所谓开放,则是针对组件间通信而言,React 允许开发者基于单向数据流完成组件通信,通信结果将改变双方或某一方内部的数据,进而对渲染结果构成影响,所以在数据层面,组件之间又是彼此开放的。

生命周期方法本质的一些理解

render 方法堪比组件的 “灵魂”,render 之外的生命周期方法可以理解为组件的 “躯干”,我们可以选择性地省略对 render 之外的任何生命周期方法内容的编写,但是 render 函数却不能省略,虚拟 DOM 的生成,更新都要仰仗 render。

React 15 生命周期

需要记忆的一些点,

  • V15 生命周期分为挂载,更新,卸载三个阶段,三个阶段对应的生命周期及执行顺序
  • 组件的更新分为两种,一是由父组件触发的更新,二是组件调用自身 setState 触发的更新。父组件触发的更新会额外调用 componentReceiveProps 生命周期,而组件自身触发的更新则直接从 shouldComponentUpdate 开始
  • componentReceiveProps 并不是由 props 的变化触发的,而是由父组件的更新触发的
  • shouldComponentUpdate 可以阻止组件 re-render 重渲染
  • 触发 componentWillUnmount 的方式有两种,一是将组件从父组件移除,二是组件中设置了 key,父组件在 render 过程中,发现 key 值和上一次不一致

React V16.3 生命周期

image.png

主要做了以下调整,

  • 挂载阶段,废弃了 componentWillMount,新增 getDrivedStateFromProps 用于从 props 派生 state 默认值
  • 更新阶段,废弃了 componentWillReceiveProps,componentWillUpdate,新增 getSnapshotBeforeUpdate 用于获取更新前的真实 DOM 和更新前后的 state & props 信息

需要注意的点有,

  • getDrivedStateFromProps 是一个静态方法,无法访问实例,应该只用于从 props 派生 state,需要返回一个对象用于更新组件 state
  • getSnapshotBeforeUpdate 的执行时机是在 render 方法之后,真实 DOM 更新之前,可以同时获取到更新前的真实 DOM 和更新前后的 state & props 信息,这在一些特殊场景十分有用,比如某些既要感知数据变化,又需要获取真实 DOM 信息的需求
  • getSnapshotBeforeUpdate 的返回值会作为第三个参数传给 componentDidUpdate

React V16.4 生命周期

image.png

16.4 在 16.3 的基础上进行了一次微调,16.3 版本只有父组件的更新会触发 getDerivedStateFromProps,16.4 中,任何因素触发的组件更新流程(包括由 this.setState 和 forceUpdate 触发的更新流程)都会触发 getDerivedStateFromProps

V16.3 后的生命周期还可以从 Fiber 层面划分为三个阶段,

  • render 阶段:纯净且没有副作用,可能会被 React 暂停、终止或重新启动
  • pre-commit 阶段:可以读取 DOM
  • commit 阶段:可以使用 DOM,运行副作用,安排更新

生命周期“废旧立新”背后的思考

废弃生命周期的共性,都是处于 render 阶段有可能被重复执行的,我们知道 Fiber 异步渲染的机制下,任务执行到一半,可能会被高优先级的任务打断,render 阶段的可能被暂停,终止和重启。

相关文章

写在最后

本文首发于我的 博客,才疏学浅,难免有错误,文章有误之处还望不吝指正!

如果有疑问或者发现错误,可以在评论区进行提问和勘误,

如果喜欢或者有所启发,欢迎 star,对作者也是一种鼓励。

(完)

campcc avatar Aug 23 '21 01:08 campcc