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()
Updating 阶段:组件的更新
如果父组件导致组件重新渲染,即使 props 没有更改,也会调用此方法。如果只想处理更改,请确保进行当前值与变更值的比较。
componentReceiveProps 并不是由 props 的变化触发的,而是由父组件的更新触发的。
这里只需要了解 shouldComponentUpdate 的基本使用即可。
Unmounting 阶段:组件的卸载
- 组件在父组件中被移除了
- 组件中设置了 key 属性
- 父组件在 render 的过程中,发现 key 值和上一次不一致
进化的生命周期方法:React 16 生命周期工作流详解
getDerivedStateFromProps 不是 componentWillMount 的替代品
componentWillMount 的存在不仅“鸡肋”而且危险,因为它并不值得被“代替”,它就应该被废弃。
getDerivedStateFromProps 有且仅有一个用途:使用 props 来派生/更新 state
React 团队直接从命名层面约束了它的用途
- 是一个静态方法
- 该方法可以接收两个参数:props和state
- 需要一个对象格式的返回值
getDerivedStateFromProps 方法对 state 的更新动作并非“覆盖”式的更新,而是针对某个属性的定向更新。
为什么使用 getDerivedStateFromProps 代替 componentWillReceiveProps?
- getDerivedStateFromProps 是作为一个试图替代 componentWillReceiveProps 而出现的
- 但两者不能直接画等号
消失的 componentWillUpdate 与新增的 getSnapshotBeforUpdate
getSnapshotBeforUpdate 的返回值会作为第三个参数给到 componentDidUpdate,它的执行时机是在 render 方法之后,真实 DOM 更新之前
同时获取到更新前的真实 DOM 和更新前后的 state&props 信息
实现一个内容会发生变化的滚动列表
要求根据滚动列表的内容是否发生变化
来决定是否要记录滚动条的当前位置
透过现在看本质:React 16 缘何两次求变?
Fiber 是 React 16 对 React 核心算法的一次重写
Fiber 会使原本同步的渲染过程变成异步的
Fiber 会将一个大一更新任务拆解为许多个小任务。
换个角度看生命周期工作流
Fiber 架构的重要特征就是可以被打断的异步渲染模式
根据“能否被打断”这一标准
React 16的生命周期被划分为了 render 和 commit 两个阶段
细说生命周期“废旧立新”背后的考虑
render 阶段是允许暂停、终止和重启的
这就层致 render 阶段的生命周期都是有可能被重复执行的
- 完全可以转移到其他生命周期里去做(尤其是 componentDidxxx)
- 在 Fiber 带来的异步渲染机制下,可能会导致非常严重的 bug
在 render 阶段里面的生命周期都可以重复执行,在 componentWillxxx 被打断+重启多次后,就会发出多个付款请求
- 即使你没有开启异步,React 15 下也有不少人能把自己“玩死”,比如 setState 搞的死循环
React 16 改造生命周期的主要动机
是为了配合 Fiber 架构带来的异步渲染机制
总结
现有的生命周期,虽然已经对方法的最佳实践做了强约束,但是仍然无法覆盖所有的“误操作”。