请解释 React 生命周期?

2022年11月16日

💎 加入 E+ 成長計畫 與超過 350+ 位軟體工程師一同在社群中成長,並且獲得更多的軟體工程學習資源

了解 React 生命周期不仅是学习 React 最基本的概念之一,也是面试经典问题。本文会介绍三大生命周期、以及在 Class Component 中常用的生命周期方法。

React 元件生命周期
React 元件生命周期
圖片來源:react-lifecycle-methods-diagram

React 元件生命周期分为三大: mountingupdatingunmounting。如果是使用 class component ,我们可以透过 React 提供出来的一些方法,在元件的生命周期执行代码,以下提供一些常见的操作方法(注意,这些方法是 class component 的生命周期方法,如果是用 functional component 的话,概念就不一样)

生命周期:mounting、updating、unmounting

Mounting

当 React 首次渲染元件,并把渲染后的节点加入 DOM 中时,我们称这时为 mounting。当 React 把节点加入到 DOM 后,浏览器会渲染并绘制画面。在这个阶段,生命周期将会依照下列的顺序呼叫这些方法:

Updating

在 mounting 后,如果一个元件的 prop 或 state 有变化时,就会触发重新渲染。重新渲染后,React 会去比较虚拟 DOM 有哪些地方改变了,然后将改变的部分更新到实际的 DOM 上。这个阶段我们称它为 updating。当一个 component 被重新渲染时,其生命周期将会依照下列的顺序呼叫这些方法:

Unmounting

当一个元件被从 DOM 中移除时,我们称之为 unmounting。这时将会呼叫:

Render 与 Commit

Mounting、Updating 和 Unmounting 这三个不同的生命周期中,分别会再细分 RenderCommit 这两个阶段。我们用这三个阶段来重新审视刚刚谈论的生命周期

Mounting

在 mounting 的 render 阶段,React 会呼叫根元件,并渲染该元件,如果该元件内有其他子元件,React 会递回地渲染。特别注意,为了能够实现 concurrent 的特点,render 阶段可能会被 React 暂停,中止或重新启动(例如有另一个元件的优先顺序比较重要,React 会暂停比较不重要的,先 render 比较重要的,之后再回来 render 比较不重要的),因为要确保暂停与重新启动不会影响 render 出的结果,React 的元件必须是纯函式(pure function),意即不能有副作用(side effect),且在每次呼叫时都会回传同样的结果。

这也是为什么,如果在 React 当中要使用到副作用,我们必须放在componentDidMount 这个生命周期方法(或useEffect),在 render 时确保是纯函式,有副作用,就等 mounting 完成后,透过componentDidMount 来执行。 (关于纯函式的详细说明,可参考《[什么是纯函式(pure function)? 为什么 React 的函式元件需要是纯函式?](https://www.explainthis.io/zh -hans/swe/react-pure-function)》一文)。

至于在 mounting 的 commit 阶段,React 会透过 appendChild() 这个 DOM API,把 render 出的结果,加入到 DOM 上面,然后实际显示到画面中。

Updating

在 updating 的 render 阶段,React 会呼叫触发更新的元件。这个过程是递归的,如果被呼叫的元件回传另一个元件,那 React 会再呼叫另一个元件,一直持续下去,直到没有元件被回传。

而到了 commit 阶段,React 会去比较 render 前与后的两个虚拟 DOM,然后把差异的部分,更新到实际的 DOM 上面。跟 mounting 时一样,在 updating 时,要确保没有副作用,如果要使用副作用,可以使用 componentDidUpdate() 这个生命周期方法 (或 useEffect)。

Updating 时还有 pre-commit 阶段

上一段落谈到 render 与 commit 阶段。如果要更细节地讨论,在元件的 updating 时还会有一个 pre-commit 阶段。顾名思义这个阶段发生在实际 commit 之前 (实际把更新后的结果同步到 DOM 之前)。

在 updating 的 pre-commit 阶段,如果使用 class component,getSnapshotBeforeUpdate()方法会被触发。这个方法让我们可以在 DOM 被真正改变之前先从其中抓取一些资讯 (例如:滚动的位置)。之所以需要这个方法,是因为在 updating 时,如果 render 阶段有异步操作,那么 render 阶段到 commit 阶段之间,可能不会是马上发生。换句话说,如果在这两个阶段间,网站的使用者做了某个操作(例如调整浏览器的视窗大小),那么在 render 阶段,透过componentWillUpdate 拿到的 DOM 资讯可能已经不是精确的。所以透过 getSnapshotBeforeUpdate 让开发者能拿到更精确的资讯。

除非是要做动画类的操作,getSnapshotBeforeUpdate() 几乎很少用到。另外,如果是使用 functional component,React 目前还没有提供可以模拟相对应的 Hook 方法(详见此讨论)。

🧵 如果你想收到最即時的內容更新,可以在 FacebookInstagram 上追蹤我們