React进阶系列之生命周期
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 生命周期
主要做了以下调整,
- 挂载阶段,废弃了 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 生命周期
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,对作者也是一种鼓励。
(完)