亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

React源碼解析之ReactDOM.render()

iKcamp / 2980人閱讀

摘要:一更新的方式有三種渲染接下來(lái),我們就來(lái)看下源碼二作用在提供的里渲染一個(gè)元素,并返回對(duì)該組件的引用常見(jiàn)的用法是這個(gè)官網(wǎng)網(wǎng)址源碼服務(wù)端使用方法渲染節(jié)點(diǎn)是讓服務(wù)端盡可能復(fù)用節(jié)點(diǎn),提高性能元素容器應(yīng)用渲染結(jié)束后,調(diào)用的函數(shù)錯(cuò)誤抓取方法本質(zhì)是返回

一、React更新的方式有三種:
(1)ReactDOM.render() || hydrate(ReactDOMServer渲染)
(2)setState
(3)forceUpdate

接下來(lái),我們就來(lái)看下ReactDOM.render()源碼

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

作用:
在提供的container里渲染一個(gè)React元素,并返回對(duì)該組件的引用

常見(jiàn)的用法是這個(gè):

ReactDOM.render(, document.getElementById("root"));

官網(wǎng)網(wǎng)址:
https://zh-hans.reactjs.org/docs/react-dom.html#render

源碼:

const ReactDOM: Object = {
  //服務(wù)端使用hydrate方法渲染節(jié)點(diǎn)
  hydrate(element: React$Node, container: DOMContainer, callback: ?Function) {
    invariant(
      isValidContainer(container),
      "Target container is not a DOM element.",
    );

    // TODO: throw or warn if we couldn"t hydrate?
    return legacyRenderSubtreeIntoContainer(
      null,
      element,
      container,
      //true是讓服務(wù)端盡可能復(fù)用節(jié)點(diǎn),提高性能
      true,
      callback,
    );
  },

  render(
    //元素
    element: React$Element,
    //容器
    container: DOMContainer,
    //應(yīng)用渲染結(jié)束后,調(diào)用的函數(shù)
    callback: ?Function,
  ) {
    //錯(cuò)誤抓取
    invariant(
      isValidContainer(container),
      "Target container is not a DOM element.",
    );

    //render方法本質(zhì)是返回了函數(shù)legacyRenderSubtreeIntoContainer
    return legacyRenderSubtreeIntoContainer(
      null,
      element,
      container,
      //render不會(huì)復(fù)用節(jié)點(diǎn),因?yàn)槭乔岸虽秩?      false,
      callback,
    );
  },

}

解析:
(1)render()方法本質(zhì)是返回了函數(shù)legacyRenderSubtreeIntoContainer()

(2)hydrate()render()的唯一區(qū)別是傳入legacyRenderSubtreeIntoContainer()的第四個(gè)參數(shù)不一樣:
hydrate()true,表示在服務(wù)端盡可能復(fù)用節(jié)點(diǎn),提高性能;
render()false,表示在瀏覽器端不會(huì)去復(fù)用節(jié)點(diǎn),而是全部替換掉。

三、legacyRenderSubtreeIntoContainer()

作用:
初始化Container

源碼:

// null, element, container, false, callback,
function legacyRenderSubtreeIntoContainer(
  parentComponent: ?React$Component,
  children: ReactNodeList,
  container: DOMContainer,
  forceHydrate: boolean,
  callback: ?Function,
) {
  // TODO: Without `any` type, Flow says "Property cannot be accessed on any
  // member of intersection type." Whyyyyyy.

  //render中一般渲染的是DOM標(biāo)簽,所以不會(huì)有_reactRootContainer存在,
  // 所以第一次渲染,root是不存在的
  let root: _ReactSyncRoot = (container._reactRootContainer: any);
  let fiberRoot;
  if (!root) {
    // Initial mount
    //創(chuàng)建一個(gè)ReactRooter
    root = container._reactRootContainer = legacyCreateRootFromDOMContainer(
      container,
      forceHydrate,
    );
    fiberRoot = root._internalRoot;

    //判斷是否有callback
    if (typeof callback === "function") {
      const originalCallback = callback;
      callback = function() {
        //根據(jù)fiberRoot獲取公共Root實(shí)例
        //就是fiberRoot.current.child.stateNode
        const instance = getPublicRootInstance(fiberRoot);
        //通過(guò)該實(shí)例instance 去調(diào)用originalCallback方法
        originalCallback.call(instance);
      };
    }
    // Initial mount should not be batched.
    //初始化安裝不應(yīng)該批量更新
    unbatchedUpdates(() => {
      //element,fiberRoot,null,callback
      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);
}

解析:
(1)由于是第一次渲染更新,所以rootnull,只需看!root的情況

(2)legacyCreateRootFromDOMContainer(container,false,)的作用是創(chuàng)建ReactRooter,稍后會(huì)講解

(3)unbatchedUpdates(fn)的簡(jiǎn)化源碼如下:

unbatchedUpdates(fn){
  return fn()
}

(4)updateContainer()的作用是更新container,稍后講解

四、legacyCreateRootFromDOMContainer(container,forceHydrate,)

作用:
創(chuàng)建一個(gè)ReactRooter

源碼:

//創(chuàng)建ReactRooter
function legacyCreateRootFromDOMContainer(
  container: DOMContainer,
  forceHydrate: boolean,
): _ReactSyncRoot {
  //是否是服務(wù)端渲染
  const shouldHydrate =
    //render的forceHydrate是false,所以會(huì)調(diào)用shouldHydrateDueToLegacyHeuristic方法來(lái)判斷是否是服務(wù)端渲染
    forceHydrate || shouldHydrateDueToLegacyHeuristic(container);
  // First clear any existing content.
  //如果不是服務(wù)端渲染的話
  if (!shouldHydrate) {
    let warned = false;
    let rootSibling;
    //循環(huán)刪除container的子節(jié)點(diǎn)
    //為什么要?jiǎng)h除?因?yàn)镽eact認(rèn)為這些節(jié)點(diǎn)是不需要復(fù)用的
    while ((rootSibling = container.lastChild)) {
 
      container.removeChild(rootSibling);
    }
  }

  // Legacy roots are not batched.
  //container是空的container,0,false
  //ReactRoot是同步的
  //sync 同步
  //async 異步
  return new ReactSyncRoot(container, LegacyRoot, shouldHydrate);
}

解析:
(1)render()forceHydratefalse,所以看shouldHydrateDueToLegacyHeuristic(container)是否返回false

shouldHydrateDueToLegacyHeuristic()

作用:
判斷是否是服務(wù)端渲染

源碼:

//判斷是否是服務(wù)端渲染
function shouldHydrateDueToLegacyHeuristic(container) {
  //獲取container的第一個(gè)節(jié)點(diǎn)(根節(jié)點(diǎn))
  //也就是 id="root" 的節(jié)點(diǎn)
  const rootElement = getReactRootElementInContainer(container);
  return !!(
    rootElement &&
    rootElement.nodeType === ELEMENT_NODE &&
    //判斷是否是服務(wù)端渲染
    rootElement.hasAttribute(ROOT_ATTRIBUTE_NAME)
  );
}

getReactRootElementInContainer()

作用:
獲取container中的第一個(gè)節(jié)點(diǎn)(或文檔節(jié)點(diǎn))

源碼:

//獲取Container里的RectRoot元素
//返回document節(jié)點(diǎn)或第一個(gè)子節(jié)點(diǎn)
function getReactRootElementInContainer(container: any) {
  if (!container) {
    return null;
  }
  //DOCUMENT_NODE 即 window.document
  if (container.nodeType === DOCUMENT_NODE) {
    return container.documentElement;
  } else {
    return container.firstChild;
  }
}

也就是說(shuō),判斷是否是服務(wù)端渲染的標(biāo)志是:
在獲取container中的第一個(gè)節(jié)點(diǎn)(或文檔節(jié)點(diǎn))后,看該節(jié)點(diǎn)是否有屬性ROOT_ATTRIBUTE_NAME

ROOT_ATTRIBUTE_NAME是什么呢?

//服務(wù)端渲染的話,會(huì)在React App的第一個(gè)元素上添加該屬性
//以標(biāo)識(shí)是服務(wù)端渲染的
export const ROOT_ATTRIBUTE_NAME = "data-reactroot";

data-reactroot

(2)由(1)可知,render()container的首節(jié)點(diǎn)是沒(méi)有data-reactroot屬性的,所以會(huì)進(jìn)行while循環(huán),依次刪除container的子節(jié)點(diǎn),刪除完畢后,new 一個(gè)ReactSyncRoot()的實(shí)例

(3)ReactSyncRoot()

作用:
創(chuàng)建ReactRoot實(shí)例

源碼:

// container,0,false
function ReactSyncRoot(
  container: DOMContainer,
  tag: RootTag,
  hydrate: boolean,
) {
  // Tag is either LegacyRoot or Concurrent Root
  const root = createContainer(container, tag, hydrate);
  this._internalRoot = root;
}

把創(chuàng)建的root作為legacyCreateRootFromDOMContainer()__internalRoot屬性

createContainer

作用:
創(chuàng)建React容器

源碼:

//創(chuàng)建React容器
export function createContainer(
  containerInfo: Container,
  tag: RootTag,
  hydrate: boolean,
): OpaqueRoot {
  //創(chuàng)建FiberRoot
  return createFiberRoot(containerInfo, tag, hydrate);
}

也就是說(shuō)legacyCreateRootFromDOMContainer()的本質(zhì)是創(chuàng)建了FilberRoot

五、updateContainer()

作用:
創(chuàng)建更新container

源碼:

//更新Container
export function updateContainer(
  element: ReactNodeList,
  container: OpaqueRoot,
  parentComponent: ?React$Component,
  callback: ?Function,
): ExpirationTime {
  const current = container.current;
  //1073741823
  const currentTime = requestCurrentTime();

  const suspenseConfig = requestCurrentSuspenseConfig();
  //計(jì)算過(guò)期時(shí)間,這是React優(yōu)先級(jí)更新非常重要的點(diǎn)
  const expirationTime = computeExpirationForFiber(
    currentTime,
    current,
    suspenseConfig,
  );
  return updateContainerAtExpirationTime(
    element,
    container,
    parentComponent,
    expirationTime,
    suspenseConfig,
    callback,
  );
}

解析:

(1)requestCurrentTime()

作用:
計(jì)算新開(kāi)始的時(shí)間

源碼不用看,只需要知道該時(shí)間,是以V8引擎上最大31位整數(shù)1073741823為根據(jù)的:

// Max 31 bit integer. The max integer size in V8 for 32-bit systems.
// Math.pow(2, 30) - 1
// 0b111111111111111111111111111111
//整型最大數(shù)值,是V8中針對(duì)32位系統(tǒng)所設(shè)置的最大值
export default 1073741823;

(2)requestCurrentSuspenseConfig()computeExpirationForFiber()以后會(huì)講解

(3)updateContainerAtExpirationTime()

作用:
每到過(guò)期時(shí)間,就更新container,過(guò)期時(shí)間單位為 10ms

源碼:

//在過(guò)期時(shí)間內(nèi),更新container
export function updateContainerAtExpirationTime(
  element: ReactNodeList,
  container: OpaqueRoot,
  parentComponent: ?React$Component,
  expirationTime: ExpirationTime,
  suspenseConfig: null | SuspenseConfig,
  callback: ?Function,
) {
  // TODO: If this is a nested container, this won"t be the root.
  const current = container.current;

  //由于parentComponent為null,所以返回空對(duì)象{}
  const context = getContextForSubtree(parentComponent);
  if (container.context === null) {
    container.context = context;
  } else {
    container.pendingContext = context;
  }
  //計(jì)劃更新Root
  return scheduleRootUpdate(
    current,
    element,
    expirationTime,
    suspenseConfig,
    callback,
  );
}

解析:

scheduleRootUpdate()

作用:
計(jì)劃更新Root

源碼:

//計(jì)劃更新Root
function scheduleRootUpdate(
  current: Fiber,
  element: ReactNodeList,
  expirationTime: ExpirationTime,
  suspenseConfig: null | SuspenseConfig,
  callback: ?Function,
) {

  //創(chuàng)建更新的時(shí)間節(jié)點(diǎn)
  const update = createUpdate(expirationTime, suspenseConfig);
  // Caution: React DevTools currently depends on this property
  // being called "element".
  update.payload = {element};

  callback = callback === undefined ? null : callback;
  if (callback !== null) {
    warningWithoutStack(
      typeof callback === "function",
      "render(...): Expected the last optional `callback` argument to be a " +
        "function. Instead received: %s.",
      callback,
    );
    update.callback = callback;
  }

  if (revertPassiveEffectsChange) {
    flushPassiveEffects();
  }
  //一整個(gè)React應(yīng)用中,會(huì)有多次更新,而這多次更新均在更新隊(duì)列中
  enqueueUpdate(current, update);
  //進(jìn)行任務(wù)調(diào)度
  //當(dāng)React進(jìn)行Update后,就要進(jìn)行調(diào)度
  //即 根據(jù)任務(wù)的優(yōu)先級(jí)去調(diào)度任務(wù)
  //先執(zhí)行優(yōu)先級(jí)高的任務(wù),
  scheduleWork(current, expirationTime);

  return expirationTime;
}

解析:
任務(wù)調(diào)度是React中最重要、復(fù)雜的內(nèi)容,之后會(huì)慢慢來(lái)解析。
這里可以看到,React將初始化的Update放入了更新隊(duì)列中,并進(jìn)行任務(wù)調(diào)度,最終返回了一個(gè)expirationTime

也就是說(shuō),updateContainer()本質(zhì)是返回了expirationTime

六、getPublicRootInstance()

作用:
獲取root實(shí)例

源碼:

//獲取root實(shí)例
export function getPublicRootInstance(
  container: OpaqueRoot,
): React$Component | PublicInstance | null {
  //看到container.current,我就想到了ref(xxx.current)
  //獲取當(dāng)前節(jié)點(diǎn)
  const containerFiber = container.current;
  if (!containerFiber.child) {
    return null;
  }
  switch (containerFiber.child.tag) {
    case HostComponent:
      return getPublicInstance(containerFiber.child.stateNode);
    default:
      return containerFiber.child.stateNode;
  }
}

解析:
由于是 React 初始化,所以container.current是沒(méi)有子節(jié)點(diǎn)的,所以該方法返回 null

七、ReactDOM.render()流程圖

總結(jié):
ReactDOM.render() 的更新步驟
(1)創(chuàng)建 ReactRoot,ReactRoot 是創(chuàng)建整個(gè)React應(yīng)用的根對(duì)象

(2)創(chuàng)建 FiberRoot 和 RootFiber

(3)創(chuàng)建更新 (創(chuàng)建更新后,就會(huì)進(jìn)入調(diào)度階段,調(diào)度階段由調(diào)度器進(jìn)行管理)

GitHub:
https://github.com/AttackXiaoJinJin/reactExplain/blob/master/react16.8.6/packages/react-dom/src/client/ReactDOM.js

(完)

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/106728.html

相關(guān)文章

  • ReactDOM.render源碼解析-1

    摘要:本文將對(duì)源碼做一個(gè)初步解析。首先在方法中校驗(yàn)參數(shù)是否合法,然后調(diào)用在中,調(diào)用拿到了的一個(gè)實(shí)例,調(diào)用拿到了,用于注入到,和作為返回值,調(diào)用開(kāi)始調(diào)度過(guò)程在中,首先清理了中的所有子節(jié)點(diǎn),然后了一個(gè)并返回是如何調(diào)度的是一個(gè)什么樣的類(lèi)的操作是在哪里 初步看了react-dom這個(gè)包的一些源碼,發(fā)現(xiàn)其比react包要復(fù)雜得多,react包中基本不存在跨包調(diào)用的情況,他所做的也僅僅是定義了React...

    _Zhao 評(píng)論0 收藏0
  • react解析: render的FiberRoot(三)

    摘要:查看創(chuàng)建核心函數(shù)源碼行調(diào)用函數(shù)創(chuàng)建是相關(guān),不用管源碼行這個(gè)指的是調(diào)用創(chuàng)建,下面我們將會(huì)說(shuō)到對(duì)象源碼行源碼行函數(shù)中,首先創(chuàng)建了一個(gè),然后又創(chuàng)建了一個(gè),它們兩者還是相互引用。 感謝 yck: 剖析 React 源碼解析,本篇文章是在讀完他的文章的基礎(chǔ)上,將他的文章進(jìn)行拆解和加工,加入我自己的一下理解和例子,便于大家理解。覺(jué)得yck寫(xiě)的真的很棒 。React 版本為 16.8.6,關(guān)于源碼的...

    muddyway 評(píng)論0 收藏0
  • React源碼解析ReactDOM.render源碼

    摘要:的創(chuàng)建組件,其實(shí)根源還是調(diào)用了編譯之后一般寫(xiě)法建議用來(lái)進(jìn)行源碼的跟蹤鏈接從源碼角度來(lái)看創(chuàng)建一個(gè)組件的過(guò)程中發(fā)生了什么。 https://github.com/jimwmg/Rea... 1 React.createClass( ) var HelloWorld = React.createClass({ render : function(){ return ...

    joywek 評(píng)論0 收藏0
  • React系列一起認(rèn)識(shí)Render Prop

    摘要:比如有個(gè)組件,它用來(lái)實(shí)時(shí)的獲取鼠標(biāo)的位置。命名空間,多個(gè)修改同一個(gè)導(dǎo)致的命名沖突。據(jù)說(shuō)源碼里面為每個(gè)組件增加路由屬性就是通過(guò)該方法好了大功完成了,歡迎一起討論學(xué)習(xí)個(gè)人博客地址意卿 1.mixins 寫(xiě)過(guò)react項(xiàng)目的應(yīng)該都碰到過(guò),不同組件復(fù)用相同代碼的問(wèn)題,在react早期使用React.createClass創(chuàng)建組件的時(shí)代,我們經(jīng)常使用的是mixins來(lái)實(shí)現(xiàn)代碼復(fù)用。比如有個(gè)組件A...

    LMou 評(píng)論0 收藏0
  • React造輪系列:對(duì)話框組件 - Dialog 思路

    摘要:本文是造輪系列第二篇。實(shí)現(xiàn)方式事件處理跟差不多,唯一多了一步就是當(dāng)點(diǎn)擊或者的時(shí)候,如果外部有回調(diào)就需要調(diào)用對(duì)應(yīng)的回調(diào)函數(shù)。 本文是React造輪系列第二篇。 1.React 造輪子系列:Icon 組件思路 本輪子是通過(guò) React + TypeScript + Webpack 搭建的,至于環(huán)境的搭建這邊就不在細(xì)說(shuō)了,自己動(dòng)手谷歌吧。當(dāng)然可以參考我的源碼。 想閱讀更多優(yōu)質(zhì)文章請(qǐng)猛戳Git...

    qianfeng 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<