欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

React?Hydrate原理源碼解析

 更新時間:2023年01月05日 09:57:04   作者:flyzz177  
這篇文章主要為大家介紹了React?Hydrate原理源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

引言

React 渲染過程,即ReactDOM.render執(zhí)行過程分為兩個大的階段:render 階段以及 commit 階段。React.hydrate渲染過程和ReactDOM.render差不多,兩者之間最大的區(qū)別就是,ReactDOM.hydraterender 階段,會嘗試復用(hydrate)瀏覽器現(xiàn)有的 dom 節(jié)點,并相互關聯(lián) dom 實例和 fiber,以及找出 dom 屬性和 fiber 屬性之間的差異。

Demo

這里,我們在 index.html 中直接返回一段 html,以模擬服務端渲染生成的 html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>Mini React</title>
    <meta name="viewport" content="width=device-width, initial-scale=1" />
  </head>
  <body>
    <div id="root"><div id="root"><div id="container"><h1 id="A">1<div id="A2">A2</div></h1><p id="B"><span id="B1">B1</span></p><span id="C">C</span></div></div></div>
  </body>
</html>

注意,root 里面的內容不能換行,不然客戶端hydrate的時候會提示服務端和客戶端的模版不一致。

新建 index.jsx:

import React from "react";
import ReactDOM from "react-dom";
class Home extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 1,
    };
  }
  render() {
    const { count } = this.state;
    return (
      <div id="container">
        <div id="A">
          {count}          <div id="A2">A2</div>
        </div>
        <p id="B">
          <span id="B1">B1</span>
        </p>
      </div>
    );
  }
}
ReactDOM.hydrate(<Home />, document.getElementById("root"));

對比服務端和客戶端的內容可知,服務端h1#A和客戶端的div#A不同,同時服務端比客戶端多了一個span#C

在客戶端開始執(zhí)行之前,即 ReactDOM.hydrate 開始執(zhí)行前,由于服務端已經返回了 html 內容,瀏覽器會立馬顯示內容。對應的真實 DOM 樹如下:

注意,這不是 fiber 樹??!

ReactDOM.render

先來回顧一下 React 渲染更新過程,分為兩大階段,五小階段:

render 階段

  • beginWork
  • completeUnitOfWork

commit 階段。

  • commitBeforeMutationEffects
  • commitMutationEffects
  • commitLayoutEffects

React 在 render 階段會根據新的 element tree 構建 workInProgress 樹,收集具有副作用的 fiber 節(jié)點,構建副作用鏈表。

特別是,當我們調用ReactDOM.render函數在客戶端進行第一次渲染時,render階段的completeUnitOfWork函數針對HostComponent以及HostText類型的 fiber 執(zhí)行以下 dom 相關的操作:

  • 調用document.createElementHostComponent類型的 fiber 節(jié)點創(chuàng)建真實的 DOM 實例?;蛘哒{用document.createTextNodeHostText類型的 fiber 節(jié)點創(chuàng)建真實的 DOM 實例
  • 將 fiber 節(jié)點關聯(lián)到真實 dom 的__reactFiber$rsdw3t27flk(后面是隨機數)屬性上。
  • 將 fiber 節(jié)點的pendingProps 屬性關聯(lián)到真實 dom 的__reactProps$rsdw3t27flk(后面是隨機數)屬性上
  • 將真實的 dom 實例關聯(lián)到fiber.stateNode屬性上:fiber.stateNode = dom。
  • 遍歷 pendingProps,給真實的dom設置屬性,比如設置 id、textContent 等

React 渲染更新完成后,React 會為每個真實的 dom 實例掛載兩個私有的屬性:__reactFiber$__reactProps$,以div#container為例:

ReactDOM.hydrate

hydrate中文意思是水合物,這樣理解有點抽象。根據源碼,我更樂意將hydrate的過程描述為:React 在 render 階段,構造 workInProgress 樹時,同時按相同的順序遍歷真實的 DOM 樹,判斷當前的 workInProgress fiber 節(jié)點和同一位置的 dom 實例是否滿足hydrate的條件,如果滿足,則直接復用當前位置的 DOM 實例,并相互關聯(lián) workInProgress fiber 節(jié)點和真實的 dom 實例,比如:

fiber.stateNode = dom;
dom.__reactProps$ = fiber.pendingProps;
dom.__reactFiber$ = fiber;

如果 fiber 和 dom 滿足hydrate的條件,則還需要找出dom.attributesfiber.pendingProps之間的屬性差異。

遍歷真實 DOM 樹的順序和構建 workInProgress 樹的順序是一致的。都是深度優(yōu)先遍歷,先遍歷當前節(jié)點的子節(jié)點,子節(jié)點都遍歷完了以后,再遍歷當前節(jié)點的兄弟節(jié)點。因為只有按相同的順序,fiber 樹同一位置的 fiber 節(jié)點和 dom 樹同一位置的 dom 節(jié)點才能保持一致

只有類型為HostComponent或者HostText類型的 fiber 節(jié)點才能hydrate。這一點也很好理解,React 在 commit 階段,也就只有這兩個類型的 fiber 節(jié)點才需要執(zhí)行 dom 操作。

fiber 節(jié)點和 dom 實例是否滿足hydrate的條件:

  • 對于類型為HostComponent的 fiber 節(jié)點,如果當前位置對應的 DOM 實例nodeTypeELEMENT_NODE,并且fiber.type === dom.nodeName,那么當前的 fiber 可以混合(hydrate)
  • 對于類型為HostText的 fiber 節(jié)點,如果當前位置對應的 DOM 實例nodeTypeTEXT_NODE,同時fiber.pendingProps不為空,那么當前的 fiber 可以混合(hydrate)

hydrate的終極目標就是,在構造 workInProgress 樹的過程中,盡可能的復用當前瀏覽器已經存在的 DOM 實例以及 DOM 上的屬性,這樣就無需再為 fiber 節(jié)點創(chuàng)建 DOM 實例,同時對比現(xiàn)有的 DOM 的attribute以及 fiber 的pendingProps,找出差異的屬性。然后將 dom 實例和 fiber 節(jié)點相互關聯(lián)(通過 dom 實例的__reactFiber$以及__reactProps$,fiber 的 stateNode 相互關聯(lián))

hydrate 過程

React 在 render 階段構造HostComponent或者HostText類型的 fiber 節(jié)點時,會首先調用 tryToClaimNextHydratableInstance(workInProgress) 方法嘗試給當前 fiber 混合(hydrate)DOM 實例。如果當前 fiber 不能被混合,那當前節(jié)點的所有子節(jié)點在后續(xù)的 render 過程中都不再進行hydrate,而是直接創(chuàng)建 dom 實例。等到當前節(jié)點所有子節(jié)點都調用completeUnitOfWork完成工作后,又會從當前節(jié)點的兄弟節(jié)點開始嘗試混合。

以下面的 demo 為例

// 服務端返回的DOM結構,這里為了直觀,我格式化了一下,按理服務端返回的內容,是不允許換行或者有空字符串的
<body>
  <div id="root">
    <div id="container">
      <h1 id="A">
        1        <div id="A2">A2</div>
      </h1>
      <p id="B">
        <span id="B1">B1</span>
      </p>
      <span id="C">C</span>
    </div>
  </div>
</body>
// 客戶端生成的內容
<div id="container">
  <div id="A">
    1    <div id="A2">A2</div>
  </div>
  <p id="B">
    <span id="B1">B1</span>
  </p>
</div>

render 階段,按以下順序:

div#container 滿足hydrate的條件,因此關聯(lián) dom,fiber.stateNode = div#container。然后使用hydrationParentFiber記錄當前混合的 fiber 節(jié)點:hydrationParentFiber = fiber。獲取下一個 DOM 實例,這里是h1#A,保存在變量nextHydratableInstance中,nextHydratableInstance = h1#A。

這里,hydrationParentFibernextHydratableInstance 都是全局變量。

  • div#Ah1#A 不能混合,這時并不會立即結束混合的過程,React 繼續(xù)對比h1#A的兄弟節(jié)點,即p#B,發(fā)現(xiàn)div#A還是不能和p#B混合,經過最多兩次對比,React 認為 dom 樹中已經沒有 dom 實例滿足和div#A這個 fiber 混合的條件,于是div#A節(jié)點及其所有子孫節(jié)點都不再進行混合的過程,此時將isHydrating設置為 false 表明div#A這棵子樹都不再走混合的過程,直接走創(chuàng)建 dom 實例。同時控制臺提示:Expected server HTML to contain a matching.. 之類的錯誤。
  • beginWork 執(zhí)行到文本節(jié)點 1 時,發(fā)現(xiàn) isHydrating = false,因此直接跳過混合的過程,在completeUnitOfWork階段直接調用document.createTextNode直接為其創(chuàng)建文本節(jié)點
  • 同樣的,beginWork 執(zhí)行到節(jié)點div#A2時,發(fā)現(xiàn)isHydrating = false,因此直接跳過混合的過程,在completeUnitOfWork階段直接調用document.createElement直接為其創(chuàng)建真實 dom 實例,并設置屬性
  • 由于div#A的子節(jié)點都已經completeUnitWork了,輪到div#A調用completeUnitWork完成工作,將hydrationParentFiber指向其父節(jié)點,即div#container這個 dom 實例。設置isHydrating = true表明可以為當前節(jié)點的兄弟節(jié)點繼續(xù)混合的過程了。div#A沒有混合的 dom 實例,因此調用document.createElement為其創(chuàng)建真實的 dom 實例。
  • p#B執(zhí)行 beginWork。由于nextHydratableInstance保存的還是h1#Adom 實例,因此p#Bh1#A對比發(fā)現(xiàn)不能復用,React 嘗試和h1#A的兄弟節(jié)點p#B對比,發(fā)現(xiàn) fiberp#B和 domp#B能混,因此將h1#A標記為刪除,同時關聯(lián) dom 實例:fiber.stateNode = p#B,保存hydrationParentFiber = fiber,nextHydratableInstance指向p#B的第一個子節(jié)點,即span#B1

...省略了后續(xù)的過程。

從上面的執(zhí)行過程可以看出,hydrate 的過程如下:

  • 調用 tryToClaimNextHydratableInstance 開始混合
  • 判斷當前 fiber 節(jié)點和同一位置的 dom 實例是否滿足混合的條件。
  • 如果當前位置的 dom 實例不滿足混合條件,則繼續(xù)比較當前 dom 的兄弟元素,如果兄弟元素和當前的 fiber 也不能混合,則當前 fiber 及其所有子孫節(jié)點都不能混合,后續(xù) render 過程將會跳過混合。直到當前 fiber 節(jié)點的兄弟節(jié)點 render,才會繼續(xù)混合的過程。

事件綁定

React在初次渲染時,不論是ReactDOM.render還是ReactDOM.hydrate,會調用createRootImpl函數創(chuàng)建fiber的容器,在這個函數中調用listenToAllSupportedEvents注冊所有原生的事件。

function createRootImpl(container, tag, options) {
  // ...
  var root = createContainer(container, tag, hydrate);
  // ...
  listenToAllSupportedEvents(container);
  // ...
  return root;
}

這里container就是div#root節(jié)點。listenToAllSupportedEvents會給div#root節(jié)點注冊瀏覽器支持的所有原生事件,比如onclick等。React合成事件一文介紹過,React采用的是事件委托的機制,將所有事件代理到div#root節(jié)點上。以下面的為例:

<div id="A" onClick={this.handleClick}>
button
<div>

我們知道React在渲染時,會將fiber的props關聯(lián)到真實的dom的__reactProps$屬性上,此時

div#A.__reactProps$ = {
  onClick: this.handleClick
}

當我們點擊按鈕時,會觸發(fā)div#root上的事件監(jiān)聽器:

function onclick(e){
  const target = e.target
  const fiberProps = target.__reactProps$
  const clickhandle = fiberProps.onClick
  if(clickhandle){
    clickhandle(e)
  }
}

這樣我們就可以實現(xiàn)事件的委托。這其中最重要的就是將fiber的props掛載到真實的dom實例的__reactProps$屬性上。因此,只要我們在hydrate階段能夠成功關聯(lián)dom和fiber,就自然也實現(xiàn)了事件的“綁定”

hydrate 源碼剖析

hydrate 的過程發(fā)生在 render 階段,commit 階段幾乎沒有和 hydrate 相關的邏輯。render 階段又分為兩個小階段:beginWorkcompleteUnitOfWork。只有HostRoot、HostComponentHostText三種類型的 fiber 節(jié)點才需要 hydrate,因此源碼只針對這三種類型的 fiber 節(jié)點剖析

beginWork

beginWork 階段判斷 fiber 和 dom 實例是否滿足混合的條件,如果滿足,則為 fiber 關聯(lián) dom 實例:fiber.stateNode = dom

function beginWork(current, workInProgress, renderLanes) {
  switch (workInProgress.tag) {
    case HostRoot:
      return updateHostRoot(current, workInProgress, renderLanes);
    case HostComponent:
      return updateHostComponent(current, workInProgress, renderLanes);
    case HostText:
      return updateHostText(current, workInProgress);
  }
}

HostRoot Fiber

HostRoot fiber 是容器root的 fiber 節(jié)點。

這里主要是判斷當前 render 是ReactDOM.render還是ReactDOM.hydrate,我們調用ReactDOM.hydrate渲染時,root.hydrate為 true。

如果是調用的ReactDOM.hydrate,則調用enterHydrationState函數進入hydrate的過程。這個函數主要是初始化幾個全局變量:

  • isHydrating。表示當前正處于 hydrate 的過程。如果當前節(jié)點及其所有子孫節(jié)點都不滿足 hydrate 的條件時,這個變量為 false
  • hydrationParentFiber。當前混合的 fiber。正常情況下,該變量和HostComponent或者HostText類型的 workInProgress 一致。
  • nextHydratableInstance。下一個可以混合的 dom 實例。當前 dom 實例的第一個子元素或者兄弟元素。

注意getNextHydratable會判斷 dom 實例是否是ELEMENT_NODE類型(對應的 fiber 類型是HostComponent)或者TEXT_NODE類型(對應的 fiber 類型是HostText)。只有ELEMENT_NODE或者HostText類型的 dom 實例才是可以 hydrate 的

function updateHostRoot(current, workInProgress, renderLanes) {
  if (root.hydrate && enterHydrationState(workInProgress)) {
    var child = mountChildFibers(workInProgress, null, nextChildren);
  }
  return workInProgress.child;
}
function getNextHydratable(node) {
  // 跳過 non-hydratable 節(jié)點.
  for (; node != null; node = node.nextSibling) {
    var nodeType = node.nodeType;
    if (nodeType === ELEMENT_NODE || nodeType === TEXT_NODE) {
      break;
    }
  }
  return node;
}
function enterHydrationState() {
  var parentInstance = fiber.stateNode.containerInfo;
  nextHydratableInstance = getNextHydratable(parentInstance.firstChild);
  hydrationParentFiber = fiber;
  isHydrating = true;
}

HostComponent

function updateHostComponent(current, workInProgress, renderLanes) {
  if (current === null) {
    tryToClaimNextHydratableInstance(workInProgress);
  }
  reconcileChildren(current, workInProgress, nextChildren, renderLanes);
  return workInProgress.child;
}

HostText Fiber

function updateHostText(current, workInProgress) {
  if (current === null) {
    tryToClaimNextHydratableInstance(workInProgress);
  }
  return null;
}

tryToClaimNextHydratableInstance

假設當前 fiberA 對應位置的 dom 為 domA,tryToClaimNextHydratableInstance 會首先調用tryHydrate判斷 fiberA 和 domA 是否滿足混合的條件:

如果 fiberA 和 domA 滿足混合的條件,則將hydrationParentFiber = fiberA;。并且獲取 domA 的第一個子元素賦值給nextHydratableInstance

如果 fiberA 和 domA 不滿足混合的條件,則獲取 domA 的兄弟節(jié)點,即 domB,調用tryHydrate判斷 fiberA 和 domB 是否滿足混合條件:

  • 如果 domB 滿足和 fiberA 混合的條件,則將 domA 標記為刪除,并獲取 domB 的第一個子元素賦值給nextHydratableInstance
  • 如果 domB 不滿足和 fiberA 混合的條件,則調用insertNonHydratedInstance提示錯誤:"Warning: Expected server HTML to contain a matching",同時將isHydrating標記為 false 退出。

這里可以看出,tryToClaimNextHydratableInstance最多比較兩個 dom 節(jié)點,如果兩個 dom 節(jié)點都無法滿足和 fiberA 混合的條件,則說明當前 fiberA 及其所有的子孫節(jié)點都無需再進行混合的過程,因此將isHydrating標記為 false。等到當前 fiberA 節(jié)點及其子節(jié)點都完成了工作,即都執(zhí)行了completeWork,isHydrating才會被設置為 true,以便繼續(xù)比較 fiberA 的兄弟節(jié)點

這里還需要注意一點,如果兩個 dom 都無法滿足和 fiberA 混合,那么nextHydratableInstance依然保存的是 domA,domA 會繼續(xù)和 fiberA 的兄弟節(jié)點比對。

function tryToClaimNextHydratableInstance(fiber) {
  if (!isHydrating) {
    return;
  }
  var nextInstance = nextHydratableInstance;
  var firstAttemptedInstance = nextInstance;
  if (!tryHydrate(fiber, nextInstance)) {
    // 如果第一次調用tryHydrate發(fā)現(xiàn)當前fiber和dom不滿足hydrate的條件,則獲取dom的兄弟節(jié)點
    // 然后調用 tryHydrate 繼續(xù)對比fiber和兄弟節(jié)點是否滿足混合
    nextInstance = getNextHydratableSibling(firstAttemptedInstance);
    if (!nextInstance || !tryHydrate(fiber, nextInstance)) {
      // 對比了兩個dom發(fā)現(xiàn)都無法和fiber混合,因此調用insertNonHydratedInstance控制臺提示錯誤
      insertNonHydratedInstance(hydrationParentFiber, fiber);
      isHydrating = false;
      hydrationParentFiber = fiber;
      return;
    }
    // 如果第一次tryHydrate不滿足,第二次tryHydrate滿足,則說明兄弟節(jié)點和當前fiber是可以混合的,此時需要刪除當前位置的dom
    deleteHydratableInstance(hydrationParentFiber, firstAttemptedInstance);
  }
  hydrationParentFiber = fiber;
  nextHydratableInstance = getFirstHydratableChild(nextInstance);
}
// 將dom實例保存在 fiber.stateNode上
function tryHydrate(fiber, nextInstance) {
  switch (fiber.tag) {
    case HostComponent: {
      if (
        nextInstance.nodeType === ELEMENT_NODE &&
        fiber.type.toLowerCase() === nextInstance.nodeName.toLowerCase()
      ) {
        fiber.stateNode = nextInstance;
        return true;
      }
      return false;
    }
    case HostText: {
      var text = fiber.pendingProps;
      if (text !== "" && nextInstance.nodeType === TEXT_NODE) {
        fiber.stateNode = nextInstance;
        return true;
      }
      return false;
    }
    default:
      return false;
  }
}

completeUnitOfWork

completeUnitOfWork 階段主要是給 dom 關聯(lián) fiber 以及 props:dom.__reactProps$ = fiber.pendingProps;dom.__reactFiber$ = fiber;同時對比fiber.pendingPropsdom.attributes的差異

function completeUnitOfWork(unitOfWork) {
  var completedWork = unitOfWork;
  do {
    var current = completedWork.alternate;
    var returnFiber = completedWork.return;
    next = completeWork(current, completedWork, subtreeRenderLanes);
    var siblingFiber = completedWork.sibling;
    if (siblingFiber !== null) {
      workInProgress = siblingFiber;
      return;
    }
    completedWork = returnFiber;
    workInProgress = completedWork;
  } while (completedWork !== null);
}
function completeWork(current, workInProgress, renderLanes) {
  switch (workInProgress.tag) {
    case HostRoot: {
      if (current === null) {
        var wasHydrated = popHydrationState(workInProgress);
        if (wasHydrated) {
          markUpdate(workInProgress);
        }
      }
      return null;
    }
    case HostComponent:
      // 第一次渲染
      if (current === null) {
        var _wasHydrated = popHydrationState(workInProgress);
        if (_wasHydrated) {
          // 如果存在差異的屬性,則將fiber副作用標記為更新
          if (prepareToHydrateHostInstance(workInProgress)) {
            markUpdate(workInProgress);
          }
        } else {
        }
      }
    case HostText: {
      var newText = newProps;
      if (current === null) {
        var _wasHydrated2 = popHydrationState(workInProgress);
        if (_wasHydrated2) {
          if (prepareToHydrateHostTextInstance(workInProgress)) {
            markUpdate(workInProgress);
          }
        }
      }
      return null;
    }
  }
}

popHydrationState

function popHydrationState(fiber) {
  if (fiber !== hydrationParentFiber) {
    return false;
  }
  if (!isHydrating) {
    popToNextHostParent(fiber);
    isHydrating = true;
    return false;
  }
  var type = fiber.type;
  if (
    fiber.tag !== HostComponent ||
    !shouldSetTextContent(type, fiber.memoizedProps)
  ) {
    var nextInstance = nextHydratableInstance;
    while (nextInstance) {
      deleteHydratableInstance(fiber, nextInstance);
      nextInstance = getNextHydratableSibling(nextInstance);
    }
  }
  popToNextHostParent(fiber);
  nextHydratableInstance = hydrationParentFiber
    ? getNextHydratableSibling(fiber.stateNode)
    : null;
  return true;
}

以下圖為例:

在 beginWork 階段對 p#B fiber 工作時,發(fā)現(xiàn) dom 樹中同一位置的h1#B不滿足混合的條件,于是繼續(xù)對比h1#B的兄弟節(jié)點,即div#C,仍然無法混合,經過最多兩輪對比后發(fā)現(xiàn)p#B這個 fiber 沒有可以混合的 dom 節(jié)點,于是將 isHydrating 標記為 false,hydrationParentFiber = fiberP#Bp#B的子孫節(jié)點都不再進行混合的過程。

div#B1fiber 沒有子節(jié)點,因此它可以調用completeUnitOfWork完成工作,completeUnitOfWork 階段調用 popHydrationState 方法,在popHydrationState方法內部,首先判斷 fiber !== hydrationParentFiber,由于此時的hydrationParentFiber等于p#B,因此條件成立,不用往下執(zhí)行。

由于p#B fiber 的子節(jié)點都已經完成了工作,因此它也可以調用completeUnitOfWork完成工作。同樣的,在popHydrationState函數內部,第一個判斷fiber !== hydrationParentFiber不成立,兩者是相等的。第二個條件!isHydrating成立,進入條件語句,首先調用popToNextHostParenthydrationParentFiber設置為p#B的第一個類型為HostComponent的祖先元素,這里是div#A fiber,然后將isHydrating設置為 true,指示可以為p#B的兄弟節(jié)點進行混合。

如果服務端返回的 DOM 有多余的情況,則調用deleteHydratableInstance將其刪除,比如下圖中div#D節(jié)點將會在div#Afiber 的completeUnitOfWork階段刪除

prepareToHydrateHostInstance

對于HostComponent類型的fiber會調用這個方法,這里只要是關聯(lián) dom 和 fiber:

  • 設置domInstance.__reactFiber$w63z5ormsqk = fiber
  • 設置domInstance.__reactProps$w63z5ormsqk = props
  • 對比服務端和客戶端的屬性
function prepareToHydrateHostInstance(fiber) {
  var domInstance = fiber.stateNode;
  var updatePayload = hydrateInstance(
    domInstance,
    fiber.type,
    fiber.memoizedProps,
    fiber
  );
  fiber.updateQueue = updatePayload;
  if (updatePayload !== null) {
    return true;
  }
  return false;
}
function hydrateInstance(domInstance, type, props, fiber) {
  precacheFiberNode(fiber, domInstance); // domInstance.__reactFiber$w63z5ormsqk = fiber
  updateFiberProps(domInstance, props); // domInstance.__reactProps$w63z5ormsqk = props
  // 比較dom.attributes和props的差異,如果dom.attributes的屬性比props多,說明服務端添加了額外的屬性,此時控制臺提示。
  // 注意,在對比過程中,只有服務端和客戶端的children屬性(即文本內容)不同時,控制臺才會提示錯誤,同時在commit階段,客戶端會糾正這個錯誤,以客戶端的文本為主。
  // 但是,如果是id不同,則客戶端并不會糾正。
  return diffHydratedProperties(domInstance, type, props);
}

這里重點講下diffHydratedProperties,以下面的demo為例:

// 服務端對應的dom
<div id="root"><div extra="server attr" id="server">客戶端的文本</div></div>
// 客戶端
render() {
  const { count } = this.state;
  return <div id="client">客戶端的文本</div>;
}

diffHydratedProperties的過程中發(fā)現(xiàn),服務端返回的id和客戶端的id不同,控制臺提示id不匹配,但是客戶端并不會糾正這個,可以看到瀏覽器的id依然是server

同時,服務端多返回了一個extra屬性,因此需要控制臺提示,但由于已經提示了id不同的錯誤,這個錯誤就不會提示。

最后,客戶端的文本和服務端的children不同,即文本內容不同,也需要提示錯誤,同時,客戶端會糾正這個文本,以客戶端的為主。

prepareToHydrateHostTextInstance

對于HostText類型的fiber會調用這個方法,這個方法邏輯比較簡單,就不詳細介紹了 務端對應的dom

<div id="root"><div extra="server attr" id="server">客戶端的文本</div></div>
// 客戶端
render() {
  const { count } = this.state;
  return <div id="client">客戶端的文本</div>;
}

diffHydratedProperties的過程中發(fā)現(xiàn),服務端返回的id和客戶端的id不同,控制臺提示id不匹配,但是客戶端并不會糾正這個,可以看到瀏覽器的id依然是server。

同時,服務端多返回了一個extra屬性,因此需要控制臺提示,但由于已經提示了id不同的錯誤,這個錯誤就不會提示。

最后,客戶端的文本和服務端的children不同,即文本內容不同,也需要提示錯誤,同時,客戶端會糾正這個文本,以客戶端的為主。

prepareToHydrateHostTextInstance

對于HostText類型的fiber會調用這個方法,這個方法邏輯比較簡單,就不詳細介紹了

以上就是React Hydrate原理源碼解析的詳細內容,更多關于React Hydrate原理的資料請關注腳本之家其它相關文章!

相關文章

  • react native 文字輪播的實現(xiàn)示例

    react native 文字輪播的實現(xiàn)示例

    這篇文章主要介紹了react native 文字輪播的實現(xiàn)示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-07-07
  • 淺談React-router v6 實現(xiàn)登錄驗證流程

    淺談React-router v6 實現(xiàn)登錄驗證流程

    本文主要介紹了React-router v6 實現(xiàn)登錄驗證流程,主要介紹了公共頁面、受保護頁面和登錄頁面,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2022-05-05
  • React進階學習之組件的解耦之道

    React進階學習之組件的解耦之道

    這篇文章主要給大家介紹了關于React進階之組件的解耦之道,文中通過詳細的示例代碼給大家介紹了組件分割與解耦的方法,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面跟著小編來一起學習學習吧。
    2017-08-08
  • react拖拽組件react-sortable-hoc的使用

    react拖拽組件react-sortable-hoc的使用

    本文主要介紹了react拖拽組件react-sortable-hoc的使用,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2023-02-02
  • React 數據獲取與性能優(yōu)化詳解

    React 數據獲取與性能優(yōu)化詳解

    這篇文章主要為大家介紹了React 數據獲取與性能優(yōu)化方法示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-10-10
  • React使用Context的一些優(yōu)化建議

    React使用Context的一些優(yōu)化建議

    Context?提供了一個無需為每層組件手動添加?props,就能在組件樹間進行數據傳遞的方法,本文為大家整理了React使用Context的一些優(yōu)化建議,希望對大家有所幫助
    2024-04-04
  • react解析html字符串方法實現(xiàn)

    react解析html字符串方法實現(xiàn)

    在使用reactjs庫的時候,會遇到將一段html的字符串,然后要將它插入頁面中以html的形式展現(xiàn),本文主要介紹了react解析html字符串方法實現(xiàn),感興趣的可以了解一下
    2023-12-12
  • useEffect中不能使用async原理詳解

    useEffect中不能使用async原理詳解

    這篇文章主要為大家介紹了useEffect中為什么不能使用async的原理詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-07-07
  • React?Native集成支付寶支付的實現(xiàn)方法

    React?Native集成支付寶支付的實現(xiàn)方法

    這篇文章主要介紹了React?Native集成支付寶支付的實現(xiàn)現(xiàn),ativeModules是JS代碼調用原生模塊的橋梁。所以,我們只需要在原生工程中集成支付寶和微信支付的sdk,然后使用NativeModules調用即可,需要的朋友可以參考下
    2022-02-02
  • ahooks正式發(fā)布React?Hooks工具庫

    ahooks正式發(fā)布React?Hooks工具庫

    這篇文章主要為大家介紹了ahooks正式發(fā)布值得擁有的React?Hooks工具庫使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-07-07

最新評論