React?Hydrate原理源碼解析
引言
React 渲染過程,即ReactDOM.render執(zhí)行過程分為兩個大的階段:render 階段以及 commit 階段。React.hydrate渲染過程和ReactDOM.render差不多,兩者之間最大的區(qū)別就是,ReactDOM.hydrate 在 render 階段,會嘗試復用(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 階段會根據(jù)新的 element tree 構建 workInProgress 樹,收集具有副作用的 fiber 節(jié)點,構建副作用鏈表。
特別是,當我們調用ReactDOM.render函數(shù)在客戶端進行第一次渲染時,render階段的completeUnitOfWork函數(shù)針對HostComponent以及HostText類型的 fiber 執(zhí)行以下 dom 相關的操作:
- 調用
document.createElement為HostComponent類型的 fiber 節(jié)點創(chuàng)建真實的 DOM 實例?;蛘哒{用document.createTextNode為HostText類型的 fiber 節(jié)點創(chuàng)建真實的 DOM 實例 - 將 fiber 節(jié)點關聯(lián)到真實 dom 的
__reactFiber$rsdw3t27flk(后面是隨機數(shù))屬性上。 - 將 fiber 節(jié)點的
pendingProps屬性關聯(lián)到真實 dom 的__reactProps$rsdw3t27flk(后面是隨機數(shù))屬性上 - 將真實的 dom 實例關聯(lián)到
fiber.stateNode屬性上:fiber.stateNode = dom。 - 遍歷
pendingProps,給真實的dom設置屬性,比如設置 id、textContent 等
React 渲染更新完成后,React 會為每個真實的 dom 實例掛載兩個私有的屬性:__reactFiber$和__reactProps$,以div#container為例:

ReactDOM.hydrate
hydrate中文意思是水合物,這樣理解有點抽象。根據(jù)源碼,我更樂意將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.attributes和fiber.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 實例nodeType為ELEMENT_NODE,并且fiber.type === dom.nodeName,那么當前的 fiber 可以混合(hydrate) - 對于類型為
HostText的 fiber 節(jié)點,如果當前位置對應的 DOM 實例nodeType為TEXT_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。
這里,hydrationParentFiber 和 nextHydratableInstance 都是全局變量。
div#A和h1#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#B和h1#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函數(shù)創(chuàng)建fiber的容器,在這個函數(shù)中調用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 階段又分為兩個小階段:beginWork 和 completeUnitOfWork。只有HostRoot、HostComponent、HostText三種類型的 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函數(shù)進入hydrate的過程。這個函數(shù)主要是初始化幾個全局變量:
- 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.pendingProps和dom.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#B。p#B的子孫節(jié)點都不再進行混合的過程。
div#B1fiber 沒有子節(jié)點,因此它可以調用completeUnitOfWork完成工作,completeUnitOfWork 階段調用 popHydrationState 方法,在popHydrationState方法內部,首先判斷 fiber !== hydrationParentFiber,由于此時的hydrationParentFiber等于p#B,因此條件成立,不用往下執(zhí)行。
由于p#B fiber 的子節(jié)點都已經完成了工作,因此它也可以調用completeUnitOfWork完成工作。同樣的,在popHydrationState函數(shù)內部,第一個判斷fiber !== hydrationParentFiber不成立,兩者是相等的。第二個條件!isHydrating成立,進入條件語句,首先調用popToNextHostParent將hydrationParentFiber設置為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-router v6 實現(xiàn)登錄驗證流程
本文主要介紹了React-router v6 實現(xiàn)登錄驗證流程,主要介紹了公共頁面、受保護頁面和登錄頁面,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2022-05-05
react拖拽組件react-sortable-hoc的使用
本文主要介紹了react拖拽組件react-sortable-hoc的使用,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2023-02-02
React 數(shù)據(jù)獲取與性能優(yōu)化詳解
這篇文章主要為大家介紹了React 數(shù)據(jù)獲取與性能優(yōu)化方法示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-10-10
React?Native集成支付寶支付的實現(xiàn)方法
這篇文章主要介紹了React?Native集成支付寶支付的實現(xiàn)現(xiàn),ativeModules是JS代碼調用原生模塊的橋梁。所以,我們只需要在原生工程中集成支付寶和微信支付的sdk,然后使用NativeModules調用即可,需要的朋友可以參考下2022-02-02

