探索 React 组件之间的生命周期

栏目: IOS · Android · 发布时间: 5年前

内容简介:React 组件的生命周期,相信大家都非常熟悉了,无非那么几个函数,官方文档已经写得非常清楚了。(那还有什么好说的?浪费感情!合上!)一般我们所讨论的,都是单个组件的生命周期。如果是多个组件之间呢?比如父子组件?兄弟组件?各个周期又是什么样的?异步路由的情况呢?前阵子新出的 Hooks 呢?有几个人敢站出来说我全知道的?(反正我是不敢)刚好也是最近遇到一些关于生命周期的问题,项目中涉及到大量的异步操作,需要清楚地知道各部分的执行顺序,借此机会整理一下。

React 组件的生命周期,相信大家都非常熟悉了,无非那么几个函数,官方文档已经写得非常清楚了。(那还有什么好说的?浪费感情!合上!)

一般我们所讨论的,都是单个组件的生命周期。如果是多个组件之间呢?比如父子组件?兄弟组件?各个周期又是什么样的?异步路由的情况呢?前阵子新出的 Hooks 呢?有几个人敢站出来说我全知道的?(反正我是不敢)

刚好也是最近遇到一些关于生命周期的问题,项目中涉及到大量的异步操作,需要清楚地知道各部分的执行顺序,借此机会整理一下。

1)在你继续之前

这篇文章并不是入门教学,如果你对 React 一点不了解的话,或许这篇文章并不适合你。

我假定你已经掌握 React 的基本知识,例如:组件的生命周期、Hooks 的基本概念、类组件和函数组件的区别 等,并用 React 开发过有一定复杂度的应用。

这里我们不讨论 shouldComponentUpdate()React.memo() 等优化手段,只考虑最原始的情况。

本文以浏览器作为目标环境,React Native 和 Electron 在基本概念上是一样的,细节上的不同不作为本文的讨论重点,

2)关于 Hooks 的生命周期

确切地说,Hooks 并不是一种新的组件类型,它只是一种代码复用的方式,并且总是伴随着函数组件一起出现。

在 Hooks 之前,函数组件是没有 state 的概念的,因而也就不存在生命周期一说,就只是一个 render 函数。Hooks 的出现,让函数组件也可以拥有 state,相应的也就引入了生命周期的概念,具体来说也就是 useEffect()useLayoutEffect() 具体何时执行的问题。

函数组件的本质是函数,而函数本身是没有生命周期的,Hooks 的出现也没有改变这一点。这里我们讨论的对象是「组件」,组件是可以有生命周期的。因此当我在后面的文字中提到 Hooks 时,我其实是在表示「使用了 Hooks 的函数组件」(虽然这个说法不是很严谨,但是这不重要,你懂我意思就好)。

3)那么我们就来做个实验吧

为了一探究竟,我写了一个Demo 来模拟一些常见的用例:父子组件、兄弟组件、同步/异步路由、类组件和 Hooks、组件初始化时的异步操作(如访问 API)等。

如果你有遇到 Demo 没覆盖到的使用场景,欢迎提 Issue。

3.1)TL,DR;

我知道大家的时间都很宝贵,赶时间的朋友可以直接看结论;时间宽裕的朋友,我们从下一节开始细聊:

render
render
useEffect

3.2)挂载过程

父子组件的挂载分为三个阶段。

第一阶段,父组件执行到自身的 render ,解析其下有哪些子组件需要渲染,并对其中同步的子组件进行创建,挨个执行各组件到 render ,生成到目前为止的 Virtual DOM 树,并 commit 到 DOM。

第二阶段,此时 DOM 节点已经生成完毕,组件挂载完成,开始后续流程。先依次触发同步子组件各自的 componentDidMount / useLayoutEffect ,最后触发父组件的。

第三阶段,如果组件使用了 useEffect ,则会在第二阶段之后触发 useEffect 。如果父子组件都使用了 useEffect ,那么子组件先触发,然后是父组件。

如果父组件中包含异步子组件,则会在父组件挂载完成后被创建。

对于兄弟组件,如果是同步路由,它们的创建顺序和在父组件中定义的出场顺序是一致的。

对于「异步的兄弟组件」,最终的加载顺序是按照 JSX 中定义的顺序,还是按照 js 文件下载完成的顺序,我暂时还不能确定。

按照我对“异步”的理解,我更倾向于认为是按照下载完成的顺序,这更符合“按需加载”的概念。

之所以会造成困扰,是因为据我目前所观察到的情况,两种顺序是一致的,我还没有遇到过后定义但先加载的情况。

大部分时候我们会以页面为单位去划分异步组件,单个页面需要加载多个异步组件的场景比较少;即便在这些少数场景中,单次需要请求的文件数量也不会很多,不至于超过浏览器的并发上限;即便超过,也会按照在父组件中定义的出场顺序去分批发起请求。考虑到单个异步组件的文件尺寸通常都很小,加载速度非常快,同一批发起的请求基本上也都是同时到达,因此大部分时候下载完成的顺序和定义的顺序是一致的。

但没遇到不代表不存在,该问题我会进一步验证,已经有结果的小伙伴也可以分享一下。

如果组件的初始化过程包含异步操作(通常在 componentDidMount()useEffect(fn, []) 中进行),这些操作何时得到响应与组件的生命周期无关,完全看异步操作本身花了多少时间。

3.3)更新过程

React 的设计遵循单向数据流模型,兄弟节点之间的通信也会经过父组件(Redux 和 Context 也是通过改变父组件传递下来的 props 实现的),因此任何两个组件之间的通信,本质上都可以归结为父组件更新导致子组件更新的情况。

父子组件的更新同样分为三个阶段。

第一、三阶段,和挂载过程基本一样,无非是第一阶段多了一个 Reconciliation 的过程,第三阶段需要先执行 useEffect 的 Cleanup 函数。

第二阶段,和挂载过程也很类似,都是子组件先于父组件,但更新比挂载涉及的函数要多一些:

  1. getSnapshotBeforeUpdate()
  2. useLayoutEffect() 的 Cleanup
  3. useLayoutEffect() / componentDidUpdate()

React 会按照上面的顺序依次执行这些函数,每个函数都是各个子组件的先执行,然后才是父组件的执行。具体说来,就是先执行各个子组件的 getSnapshotBeforeUpdate() ,然后是父组件的 getSnapshotBeforeUpdate() ,再然后是各个子组件的 componentDidUpdate() ,父组件的 componentDidUpdate() ,以此类推。

这里我们把类组件和 Hooks 的生命周期函数放在了一起,因为父子组件可以是这两种组件类型的任意排列组合。实际渲染时不一定每一个函数都有用到,只会调用组件实际拥有的函数。

3.4)卸载过程

卸载过程涉及到 componentWillUnmount()useEffect() 的 Cleanup、 useLayoutEffect() 的 Cleanup 这三种函数,顺序固定为父组件的先执行,子组件按照在 JSX 中定义的顺序依次执行各自的方法。

注意,此时的 Cleanup 函数会按照在代码中定义的顺序先后执行,与函数本身的特性无关。

如果卸载旧组件的同时伴随有新组件的创建,新组件会先被创建并执行完 render ,然后卸载不需要的旧组件,最后新组件执行挂载完成的回调。

4)Hooks 的特别之处

根据 React 的官方文档, useEffect()useLayoutEffect() 都是等效于 componentDidUpdate() / componentDidMount() 的存在,但实际上两者在一些细节上还是有所不同:

4.1)先来未必先走

useLayoutEffect() 永远比 useEffect() 先执行,即便在你的代码中 useEffect() 是写在前面的。所以 useLayoutEffect() 才是事实上和 componentDidUpdate() / componentDidMount() 平起平坐的存在。

useEffect() 会在父子组件的 componentDidUpdate() / componentDidMount() 都触发之后才被触发。当父子组件都用到 useEffect() 时,子组件中的会比父组件中的先触发。

4.2)不团结的 Cleanup

同样都拥有 Cleanup 函数, useLayoutEffect() 和它的 Cleanup 未必是挨着的。

当父组件是 Hooks、子组件是 Class 时,能够很明显看出, useLayoutEffect() 的 Cleanup 会在 getSnapshotBeforeUpdate()componentDidUpdate() 之间被调用,而 useLayoutEffect() 则是和 componentDidUpdate() 同级,按照更新过程的顺序被调用。

Hooks 作为子组件时也是这么个过程,只是没有了子组件,看上去不那么明显罢了。

useEffect() 就不一样,它和它的 Cleanup 紧密团结在一起,每次执行都是前后脚一起的,从不分离。


以上所述就是小编给大家介绍的《探索 React 组件之间的生命周期》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

网络营销实战密码

网络营销实战密码

昝辉Zac / 电子工业出版社 / 2009.1 / 56.00元

本书是作者几年来网络营销实战的总结,与其他网络营销书籍最大不同之处是:只专注于实战,不谈理论。本书分三部分详细介绍了网络营销实用策略和技巧,并分析了大量实战案例。第一部分介绍市场与产品研究,包括用户、市场和竞争对手的调查;产品、目标市场的确定;价格策略;赢利模式等。第二部分讨论以网络营销为导向的网站设计,包括怎样在网站上卖东西、提高转化率,以及网站目标设定等。第三部分研究怎样给网站带来流量,详细讨......一起来看看 《网络营销实战密码》 这本书的介绍吧!

MD5 加密
MD5 加密

MD5 加密工具

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具