iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 6
0
Modern Web

ReactJS 疑難排解系列 第 6

ReactJS 疑難排解:什麼是 reconciliation

在了解 reconciliation 前,我們先說說 react 是如何去操作 DOM 的

在 javaScript 中,我們可以利用 createElement, createTextNode, appendChild, removeChild 來操作 DOM 元素,其實 React 也不例外,只不過這部分「操作」的邏輯已經被 react-reconciler 封裝了(實作在 react-dom 裡),只要注入一些 handler 便可以用自定義的方式渲染 React Component,因此我們可以讓 react 使用在非 DOM 的環境(host)下。如果對這部分有興趣,可以參考 awesome-react-renderer 這個 repo,裡面有很多很酷的 renderer。

正因為封裝好 append, remove 這些 handler,react reconciler 便可以抽象化的方式去操作 Component,把邏輯專注在 diff 上面。

Reconciler

從一個 Component 的角度來看,React 的渲染流程大致如下:

      |----------|          |---------|            |--------|
      |  render  |   --->   | prepare |    --->    | commit |
      |----------|          |---------|            |--------|

    |___  user   __|      |_ react-reconciler <==  react-dom __|

可以發現從進 render() 之後的過程全都交由 reconciler 來進行操作

我們可以注意到我旁邊寫了一個 <== react-dom,正如同上面所寫 react-reconciler 負責了 render 後的兩個環節(prepare, commit)。但實際上他只是負責 invoke 外面 host 丟進來的 config 而已,因此這裡實際中呼叫的都是 react-dom 注入進來的 handler。


另外,我們從 react-dom 的 source code 中可以發現會有 diff 做在 prepareUpdate(其中一個 host config) 中,他會 return diff 的結果,交由 commitUpdate 來做 DOM 的渲染。

/**
 * excerpt from react-dom source code
 */
export function prepareUpdate(
  domElement: Instance,
  type: string,
  oldProps: Props,
  newProps: Props,
  rootContainerInstance: Container,
  hostContext: HostContext,
): null | Array<mixed> {
  ...
  return diffProperties(
    domElement,
    type,
    oldProps,
    newProps,
    rootContainerInstance,
  );
}

Diff 算法

reconciler 使用的是 heuristic 的算法,建立在以下兩個假設上:

  1. 兩個不同類型的 element 會產生出不同的 tree
  2. 開發者可以通過 key prop 來指出哪些子 element 在不同的 render 下能保持不變

也正因為這些前提,才可以利用較簡單的規則來比較 vDOM node,並決定要進行何種操作。

至於詳細的規則,大家可以直接看 ReactJS 官方的 Docs,這裡就不贅述了

結語

其實我覺得上面的 diff 感覺和更上面 prepare 內的 diff 不大一樣
看起來一個是基於 vDOM tree,一個是基於 component
越想越不明白了,明天有空再來研究一下 reconciler 的 source code 好了
希望下一篇能夠解惑 (`・ω・´)


上一篇
ReactJS 疑難排解:為什麼用了 React.memo 還是一直 re-render
下一篇
ReactJS 疑難排解:React Fiber
系列文
ReactJS 疑難排解8

1 則留言

0

我要留言

立即登入留言