前言

最近做了不少面試

經過各種自信心的打擊之後

發現自己對 react 其實沒有想像中那麼了解

明明每天跟他朝夕相處,以為很懂他

記錄一下其中一個被問卻觀念錯誤的問題

Render and Commit

不是什麼複雜的概念

就只是考 react 一個蠻基本的概念

在 react docs 就有的 Render phase 和 Commit phase


import {useEffect} from "react"

export default function Parent() {

console.log('I am parent component')
 
 useEffect(() => {
  console.log('I am parent useEffect')
 }, [])

  return <div>I am parent
    <Child />
  </div>
}

function Child() {

console.log('I am child component')

 useEffect(() => {
  console.log('I am child useEffect')
 }, [])
  return <>I am child</>
}

以上方程式碼作為範例

Render Phase

先說明一下名詞

這裡的 render 和一般我們熟知的 react 渲染其實不是一個意思

render phase 就是 react 在計算的一個過程

利用遞迴最終產生一個 React Element 樹狀結構結果

如果是 rerender ,一樣會產生一個 React element

差異就是會用當前 Fiber tree 做比對,來確認需要更新的 dom

diffing 就是在這裡完成的

在這個 phase 中,就會由上到下執行所有的 component

因此回到上面範例就可以知道

最先 console 出來的兩個順序就是

// I am parent component
// I am child component

Commit Phase

根據所獲得的 React element tree 做 Dom 節點的創建

如果是 rerender 的 commit phase,就是會「更新」Dom 節點

那當然也是透過 react 計算出最少操作需要的節點

而 commit phase 是遵循「深度優先」原則

所以結果想當然會是

// I am child useEffect
// I am parent useEffect

useLayoutEffect & useEffect

由 commit phase 也可以理解到這兩者的差異

useLayoutEffect 是在 commit phase 同步執行的,也就是在 DOM 更新後,但在瀏覽器繪製前

useEffect 則是在 commit phase 後執行

useLayoutEffect 多用來針對 dom 的測量或更新用的