ReactDOM的render和Fiber源碼解析

render()

ReactDOM.render(element, container[, callback])

在提供的 container 里渲染一個 React 元素磁携,并返回對該組件的引用(或者針對無狀態(tài)組件返回 null)。

如果 React 元素之前已經(jīng)在 container 里渲染過膝舅,這將會對其執(zhí)行更新操作,并僅會在必要時改變 DOM 以映射最新的 React 元素窑多。

如果提供了可選的回調(diào)函數(shù)铸史,該回調(diào)將在組件被渲染或更新之后被執(zhí)行。

注意:

ReactDOM.render() 會控制你傳入容器節(jié)點(diǎn)里的內(nèi)容怯伊。當(dāng)首次調(diào)用時琳轿,容器節(jié)點(diǎn)里的所有 DOM 元素都會被替換,后續(xù)的調(diào)用則會使用 React 的 DOM 差分算法(DOM diffing algorithm)進(jìn)行高效的更新耿芹。

ReactDOM.render() 不會修改容器節(jié)點(diǎn)(只會修改容器的子節(jié)點(diǎn))崭篡。可以在不覆蓋現(xiàn)有子節(jié)點(diǎn)的情況下吧秕,將組件插入已有的 DOM 節(jié)點(diǎn)中琉闪。

ReactDOM.render() 目前會返回對根組件 ReactComponent 實(shí)例的引用。 但是砸彬,目前應(yīng)該避免使用返回的引用颠毙,因?yàn)樗菤v史遺留下來的內(nèi)容,而且在未來版本的 React 中砂碉,組件渲染在某些情況下可能會是異步的蛀蜜。 如果你真的需要獲得對根組件 ReactComponent 實(shí)例的引用,那么推薦為根元素添加 callback ref增蹭。

使用 ReactDOM.render() 對服務(wù)端渲染容器進(jìn)行 hydrate 操作的方式已經(jīng)被廢棄滴某,并且會在 React 17 被移除。作為替代滋迈,請使用 hydrate()霎奢。

packages/react-dom/ReactDOM.js 源碼解析

時序圖.png
render(
    element,
    container,
    callback,
  ) {
    invariant(
      isValidContainer(container),
      'Target container is not a DOM element.',
    );
    if (__DEV__) {
      warningWithoutStack(
        !container._reactHasBeenPassedToCreateRootDEV,
        'You are calling ReactDOM.render() on a container that was previously ' +
          'passed to ReactDOM.%s(). This is not supported. ' +
          'Did you mean to call root.render(element)?',
        enableStableConcurrentModeAPIs ? 'createRoot' : 'unstable_createRoot',
      );
    }
    return legacyRenderSubtreeIntoContainer(
      null,
      element,
      container,
      false,
      callback,
    );
  }

ReactDOM.render方法中調(diào)用了legacyRenderSubtreeIntoContainer方法:

function legacyRenderSubtreeIntoContainer(
  parentComponent,
  children,
  container,
  forceHydrate,
  callback,
) {
  if (__DEV__) {
    topLevelUpdateWarnings(container);
    warnOnInvalidCallback(callback === undefined ? null : callback, 'render');
  }

  // TODO: Without `any` type, Flow says "Property cannot be accessed on any
  // member of intersection type." Whyyyyyy.
  let root = container._reactRootContainer;
  let fiberRoot;
  if (!root) {
    // Initial mount
    root = container._reactRootContainer = legacyCreateRootFromDOMContainer(
      container,
      forceHydrate,
    );
    fiberRoot = root._internalRoot;
    if (typeof callback === 'function') {
      const originalCallback = callback;
      callback = function() {
        const instance = getPublicRootInstance(fiberRoot);
        originalCallback.call(instance);
      };
    }
    // Initial mount should not be batched.
    unbatchedUpdates(() => {
      updateContainer(children, fiberRoot, parentComponent, callback);
    });
  } else {
    fiberRoot = root._internalRoot;
    if (typeof callback === 'function') {
      const originalCallback = callback;
      callback = function() {
        const instance = getPublicRootInstance(fiberRoot);
        originalCallback.call(instance);
      };
    }
    // Update
    updateContainer(children, fiberRoot, parentComponent, callback);
  }
  return getPublicRootInstance(fiberRoot);
}
  • 如果沒有container._reactRootContainer值,則說明是第一次掛載到該container下饼灿,這時候幕侠,react在執(zhí)行legacyCreateRootFromDOMContainer的會清空該dom下的子元素,并返回一個ReactSyncRoot類型的對象碍彭。并且第一次掛載的時候晤硕,shouldHydrate是為false的。
  • 經(jīng)過legacyCreateRootFromDOMContainer 之后硕旗,container的數(shù)據(jù)是這樣的:
container._reactRootContainer = ReactRoot (from legacyCreateRootFromDOMContainer);
container._reactRootContainer._internalRoot = FiberRoot
container._reactRootContainer._internalRoot.current = FiberNode (HostRoot)
container._reactRootContainer._internalRoot.current.stateNode = container._reactRootContainer._internalRoot = FiberRoot

FiberRoot和Fiber的數(shù)據(jù)結(jié)構(gòu):

function FiberRootNode(containerInfo, tag, hydrate) {
  this.tag = tag;
  this.current = null;
  this.containerInfo = containerInfo;
  this.pendingChildren = null;
  this.pingCache = null;
  this.finishedExpirationTime = NoWork;
  this.finishedWork = null;
  this.timeoutHandle = noTimeout;
  this.context = null;
  this.pendingContext = null;
  this.hydrate = hydrate;
  this.firstBatch = null;
  this.callbackNode = null;
  this.callbackExpirationTime = NoWork;
  this.firstPendingTime = NoWork;
  this.lastPendingTime = NoWork;
  this.pingTime = NoWork;

  if (enableSchedulerTracing) {
    this.interactionThreadID = unstable_getThreadID();
    this.memoizedInteractions = new Set();
    this.pendingInteractionMap = new Map();
  }
}

function FiberNode(
  tag,
  pendingProps,
  key,
  mode,
) {
  // Instance
  this.tag = tag;
  this.key = key;
  this.elementType = null;
  this.type = null;
  this.stateNode = null;

  // Fiber
  this.return = null;
  this.child = null;
  this.sibling = null;
  this.index = 0;

  this.ref = null;

  this.pendingProps = pendingProps;
  this.memoizedProps = null;
  this.updateQueue = null;
  this.memoizedState = null;
  this.dependencies = null;

  this.mode = mode;

  // Effects
  this.effectTag = NoEffect;
  this.nextEffect = null;

  this.firstEffect = null;
  this.lastEffect = null;

  this.expirationTime = NoWork;
  this.childExpirationTime = NoWork;

  this.alternate = null;
}

接下去就是關(guān)鍵的updateContainer函數(shù)了窗骑。這個函數(shù)牽涉的面比較多,我們下一片再說漆枚。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末创译,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子墙基,更是在濱河造成了極大的恐慌软族,老刑警劉巖刷喜,帶你破解...
    沈念sama閱讀 219,039評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異立砸,居然都是意外死亡掖疮,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評論 3 395
  • 文/潘曉璐 我一進(jìn)店門颗祝,熙熙樓的掌柜王于貴愁眉苦臉地迎上來浊闪,“玉大人,你說我怎么就攤上這事螺戳「楸觯” “怎么了?”我有些...
    開封第一講書人閱讀 165,417評論 0 356
  • 文/不壞的土叔 我叫張陵倔幼,是天一觀的道長盖腿。 經(jīng)常有香客問我,道長损同,這世上最難降的妖魔是什么翩腐? 我笑而不...
    開封第一講書人閱讀 58,868評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮膏燃,結(jié)果婚禮上茂卦,老公的妹妹穿的比我還像新娘。我一直安慰自己蹄梢,他們只是感情好疙筹,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,892評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著禁炒,像睡著了一般。 火紅的嫁衣襯著肌膚如雪霍比。 梳的紋絲不亂的頭發(fā)上幕袱,一...
    開封第一講書人閱讀 51,692評論 1 305
  • 那天,我揣著相機(jī)與錄音悠瞬,去河邊找鬼们豌。 笑死,一個胖子當(dāng)著我的面吹牛浅妆,可吹牛的內(nèi)容都是我干的望迎。 我是一名探鬼主播,決...
    沈念sama閱讀 40,416評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼凌外,長吁一口氣:“原來是場噩夢啊……” “哼辩尊!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起康辑,我...
    開封第一講書人閱讀 39,326評論 0 276
  • 序言:老撾萬榮一對情侶失蹤摄欲,失蹤者是張志新(化名)和其女友劉穎轿亮,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體胸墙,經(jīng)...
    沈念sama閱讀 45,782評論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡我注,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,957評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了迟隅。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片但骨。...
    茶點(diǎn)故事閱讀 40,102評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖智袭,靈堂內(nèi)的尸體忽然破棺而出嗽冒,到底是詐尸還是另有隱情,我是刑警寧澤补履,帶...
    沈念sama閱讀 35,790評論 5 346
  • 正文 年R本政府宣布添坊,位于F島的核電站,受9級特大地震影響箫锤,放射性物質(zhì)發(fā)生泄漏贬蛙。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,442評論 3 331
  • 文/蒙蒙 一谚攒、第九天 我趴在偏房一處隱蔽的房頂上張望阳准。 院中可真熱鬧,春花似錦馏臭、人聲如沸野蝇。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽绕沈。三九已至,卻和暖如春帮寻,著一層夾襖步出監(jiān)牢的瞬間乍狐,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評論 1 272
  • 我被黑心中介騙來泰國打工固逗, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留浅蚪,地道東北人。 一個月前我還...
    沈念sama閱讀 48,332評論 3 373
  • 正文 我出身青樓烫罩,卻偏偏與公主長得像惜傲,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子贝攒,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,044評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 40盗誊、React 什么是React?React 是一個用于構(gòu)建用戶界面的框架(采用的是MVC模式):集中處理VIE...
    萌妹撒閱讀 1,017評論 0 1
  • 原教程內(nèi)容詳見精益 React 學(xué)習(xí)指南,這只是我在學(xué)習(xí)過程中的一些閱讀筆記浊伙,個人覺得該教程講解深入淺出撞秋,比目前大...
    leonaxiong閱讀 2,839評論 1 18
  • 以下內(nèi)容是我在學(xué)習(xí)和研究React時,對React的特性嚣鄙、重點(diǎn)和注意事項(xiàng)的提取吻贿、精練和總結(jié),可以做為React特性...
    科研者閱讀 8,234評論 2 21
  • React基礎(chǔ) React組件化編程 Create React App 創(chuàng)建React 前端工程 題外話題:頁面性...
    BeautifulHao閱讀 1,543評論 0 3
  • 有點(diǎn)急躁哑子,感覺一大堆事情涌來舅列,想做的事情太多
    冰淇淋很餓閱讀 164評論 1 0