前言:
仍是在 React源碼解析之workLoop 中,有一段HostComponent
和HostText
的更新:javascript
case HostComponent:
//更新 DOM 標籤
return updateHostComponent(current, workInProgress, renderExpirationTime);
case HostText:
//更新文本節點
return updateHostText(current, workInProgress);
複製代碼
本文就解析下updateHostComponent()
和updateHostText()
方法html
1、updateHostComponent
做用:
更新DOM
標籤java
源碼:node
//更新 DOM 標籤
function updateHostComponent(current, workInProgress, renderExpirationTime) {
//===暫時跳過 context
pushHostContext(workInProgress);
//判斷可否複用服務端渲染的節點
if (current === null) {
tryToClaimNextHydratableInstance(workInProgress);
}
const type = workInProgress.type;
const nextProps = workInProgress.pendingProps;
const prevProps = current !== null ? current.memoizedProps : null;
let nextChildren = nextProps.children;
//判斷該節點是不是文本節點
const isDirectTextChild = shouldSetTextContent(type, nextProps);
//若是是文本節點的話(即裏面再也不嵌套其餘類型的節點)
if (isDirectTextChild) {
// We special case a direct text child of a host node. This is a common
// case. We won't handle it as a reified child. We will instead handle
// this in the host environment that also have access to this prop. That
// avoids allocating another HostText fiber and traversing it.
//沒必要渲染子節點,直接顯示其文本便可
nextChildren = null;
}
//若是以前節點不爲空且爲文本節點,但如今更新爲其餘類型的節點的話
else if (prevProps !== null && shouldSetTextContent(type, prevProps)) {
// If we're switching from a direct text child to a normal child, or to
// empty, we need to schedule the text content to be reset.
//重置文本節點
workInProgress.effectTag |= ContentReset;
}
//只有 HostComponent 和 ClassComponent 有使用該方法
//由於只有這兩個 Component 能拿到 DOM 實例
markRef(current, workInProgress);
// Check the host config to see if the children are offscreen/hidden.
//若是該節點上設置了 hidden 屬性,而且是異步渲染(ConcurrentMode)的話,那麼它將最後更新
//關於 ConcurrentMode 模式,請參考:https://zh-hans.reactjs.org/docs/concurrent-mode-intro.html
if (
workInProgress.mode & ConcurrentMode &&
renderExpirationTime !== Never &&
shouldDeprioritizeSubtree(type, nextProps)
) {
if (enableSchedulerTracing) {
markSpawnedWork(Never);
}
// Schedule this fiber to re-render at offscreen priority. Then bailout.
//優先級最低,即最後更新
workInProgress.expirationTime = workInProgress.childExpirationTime = Never;
return null;
}
//將 ReactElement 變成 fiber對象,並更新,生成對應 DOM 的實例,並掛載到真正的 DOM 節點上
reconcileChildren(
current,
workInProgress,
nextChildren,
renderExpirationTime,
);
return workInProgress.child;
}
複製代碼
解析:
(1) context
相關的之後講
(2) tryToClaimNextHydratableInstance()
方法的做用是判斷可否複用服務端渲染的root
內部已有的DOM
節點
(3) shouldSetTextContent()
的做用是判斷該節點是不是文本節點:react
//判斷是不是文本節點
export function shouldSetTextContent(type: string, props: Props): boolean {
return (
type === 'textarea' ||
type === 'option' ||
type === 'noscript' ||
typeof props.children === 'string' ||
typeof props.children === 'number' ||
(typeof props.dangerouslySetInnerHTML === 'object' &&
props.dangerouslySetInnerHTML !== null &&
props.dangerouslySetInnerHTML.__html != null)
);
}
複製代碼
type
應該表示html
裏的標籤,如<textarea>
、<option>
、noscript
props.children
指節點裏的內容是不是字符串仍是數字dangerouslySetInnerHTML
即innerHTML
,裏面內容也是字符串web
關於dangerouslySetInnerHTML
的介紹與使用,請參考:zh-hans.reactjs.org/docs/dom-el…app
也就是說,一旦shouldSetTextContent()
判斷爲true
,就肯定該節點爲文本節點dom
(4) 若是isDirectTextChild
爲true
,則表示其內部是文本,故直接渲染便可,nextChildren
置爲null
,後面講到的updateHostText()
的源碼也是相似的異步
(5) 若是以前節點不爲空且爲文本節點,但如今更新爲其餘類型的節點的話,則設一個ContentReset
的標籤oop
(6) markRef
的做用是標記ref
只有HostComponent
和ClassComponent
有使用該方法,由於只有這兩個Component
能直接獲取到DOM
實例的引用:
//標記 ref
function markRef(current: Fiber | null, workInProgress: Fiber) {
const ref = workInProgress.ref;
if (
(current === null && ref !== null) ||
(current !== null && current.ref !== ref)
) {
// Schedule a Ref effect
workInProgress.effectTag |= Ref;
}
}
複製代碼
若是是第一次渲染而且設置了 ref 引用的話,或者不是第一次渲染,可是 ref 的引用發生變化的話,則設置Ref
標籤
(7) 若是設置了ConcurrentMode
模式,而且渲染的優先級不是最低的Never
的話,則將該節點的更新優先級重置爲最低優先級Never
,return null
則表示不更新
ConcurrentMode
模式,個人理解是異步渲染 UI(隨時暫停,隨時切換),應該是 React 17 會發布到穩定版的新特性,對此模式感興趣的同窗,請參考:zh-hans.reactjs.org/docs/concur…
(8) 若是 (7) 條件不成立的話,則往下執行reconcileChildren()
,將 ReactElement 變成 fiber對象,並更新,生成對應 DOM 的實例,並掛載到真正的 DOM 節點上
關於reconcileChildren()
的講解,請參考:React源碼解析之FunctionComponent(上)
2、updateHostText
做用:
更新 host 文本節點
源碼:
//更新 host 文本節點
function updateHostText(current, workInProgress) {
if (current === null) {
tryToClaimNextHydratableInstance(workInProgress);
}
// Nothing to do here. This is terminal. We'll do the completion step
// immediately after.
//沒有對 DOM 進行操做的地方,直接渲染出來便可
return null;
}
複製代碼
解析:
跟1、updateHostComponent
中的(4)
類似,文本節點直接渲染出來便可。
(完)