React源碼解析之updateHostComponent和updateHostText

前言:
仍是在 React源碼解析之workLoop 中,有一段HostComponentHostText的更新: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指節點裏的內容是不是字符串仍是數字
dangerouslySetInnerHTMLinnerHTML,裏面內容也是字符串web

關於dangerouslySetInnerHTML的介紹與使用,請參考:zh-hans.reactjs.org/docs/dom-el…app

也就是說,一旦shouldSetTextContent()判斷爲true,就肯定該節點爲文本節點dom

(4) 若是isDirectTextChildtrue,則表示其內部是文本,故直接渲染便可,nextChildren置爲null,後面講到的updateHostText()的源碼也是相似的異步

(5) 若是以前節點不爲空且爲文本節點,但如今更新爲其餘類型的節點的話,則設一個ContentReset的標籤oop

(6) markRef的做用是標記ref
只有HostComponentClassComponent有使用該方法,由於只有這兩個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的話,則將該節點的更新優先級重置爲最低優先級Neverreturn 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)類似,文本節點直接渲染出來便可。


(完)

相關文章
相關標籤/搜索