成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

React 渲染過(guò)程

andong777 / 2670人閱讀

摘要:對(duì)如何生成等感興趣的可以去探究,這里不做具體的討論,本文只是想總結(jié)下整個(gè)渲染過(guò)程。最終的過(guò)程是以上是渲染的一個(gè)基本流程,下一篇計(jì)劃總結(jié)下更新的流程,即后發(fā)生了什么。

程序假設(shè)有如下 jsx

class Form extends React.Component {
  constructor() {
    super();
  }
  render() {
    return (
        
); } } ReactDOM.render( (
CLICK ME
), document.getElementById("main"))

拿 ReactDOM render 的部分(

...
)為例,用 babel 把 jsx 轉(zhuǎn)成 js 后得到如下代碼:

React.createElement( "div", {
  className: "test"
  },
  React.createElement( "span",
    { onClick: function(){} },
    "CLICK ME"
  ),
  React.createElement(Form, null)
)

這里看下 API: React.createElement(component, props, ...children)。它生成一個(gè) js 的對(duì)象,這個(gè)對(duì)象是用來(lái)代表一個(gè)真實(shí)的 dom node,這個(gè) js 的對(duì)象就是我們俗稱的虛擬dom。(虛擬 dom 的意思是用 js 對(duì)象結(jié)構(gòu)模擬出 html 中 dom 結(jié)構(gòu),批量的增刪改查先直接操作 js 對(duì)象,最后更新到真正的 dom 樹(shù)上。因?yàn)橹苯硬僮?js 對(duì)象的速度要比操作 dom 的那些 api 要快。) 譬如根元素

生成對(duì)應(yīng)出來(lái)的虛擬 dom 是:

{
  type: "div",
  props: {
    className: "test",
    children: []
  }
}

除了一些 dom 相關(guān)的屬性,虛擬 dom 對(duì)象還包括些 React 自身需要的屬性,譬如:ref,key。最終示例中的 jsx 生成出來(lái)的虛擬 dom,大致如下:

{
  type: "div",
  props: {
    className: "xxx",
    children: [ {
      type: "span",
      props: {
        children: [ "CLICK ME" ]
      },
      ref:
      key:
    }, {
      type: Form,
      props: {
        children: []
      },
      ref:
      key:
    } ] | Element
  }
  ref: "xxx",
  key: "xxx"
}

有了虛擬 dom,接下來(lái)的工作就是把這個(gè)虛擬 dom 樹(shù)真正渲染成一個(gè) dom 樹(shù)。React 的做法是針對(duì)不同的 type 構(gòu)造相應(yīng)的渲染對(duì)象,渲染對(duì)象提供一個(gè) mountComponent 方法(負(fù)責(zé)把對(duì)應(yīng)的某個(gè)虛擬 dom 的節(jié)點(diǎn)生成成具體的 dom node),然后循環(huán)迭代整個(gè) vdom tree 生成一個(gè)完整的 dom node tree,最終插入容器節(jié)點(diǎn)。查看源碼你會(huì)發(fā)現(xiàn)如下代碼:

// vdom 是第3步生成出來(lái)的虛擬 dom 對(duì)象
var renderedComponent = instantiateReactComponent( vdom );
// dom node
var markup = renderedComponent.mountComponent();
// 把生成的 dom node 插入到容器 node 里面,真正在頁(yè)面上顯示出來(lái)
// 下面是偽代碼,React 的 dom 操作封裝在 DOMLazyTree 里面
containerNode.appendChild( markup );

instantiateReactComponent 傳入的是虛擬 dom 節(jié)點(diǎn),這個(gè)方法做的就是根據(jù)不同的 type 調(diào)用如下方法生成渲染對(duì)象:

// 如果節(jié)點(diǎn)是字符串或者數(shù)字
return ReactHostComponent.createInstanceForText( vdom(string|number) );
// 如果節(jié)點(diǎn)是宿主內(nèi)置節(jié)點(diǎn),譬如瀏覽器的 html 的節(jié)點(diǎn)
return ReactHostComponent.createInternalComponent( vdom );
// 如果是 React component 節(jié)點(diǎn)
return new ReactCompositeComponentWrapper( vdom );

ReactHostComponent.createXXX 也只是一層抽象,不是最終的的渲染對(duì)象,這層抽象屏蔽了宿主。譬如手機(jī)端(React native)和瀏覽器中同樣調(diào)用 ReactHostComponent.createInternalComponent( vdom ); 他生成的最終的渲染對(duì)象是不同的,我們當(dāng)前只討論瀏覽器環(huán)境。字符串和數(shù)字沒(méi)有什么懸念,在這里我們就不深入探討了,再進(jìn)一步看,div 等 html 的原生 dom 節(jié)點(diǎn)對(duì)應(yīng)的渲染對(duì)象是 ReactDOMComponent 的實(shí)例。如何把 { type:"div", ... } 生成一個(gè) dom node 就在這個(gè)類(的 mountComponent 方法)里面。(對(duì)如何生成 div、span、input、select 等 dom node 感興趣的可以去探究 ReactDOMComponent,這里不做具體的討論,本文只是想總結(jié)下 React 整個(gè)渲染過(guò)程。下面只給出一個(gè)最簡(jiǎn)示例代碼:

class ReactDOMComponent {
  constructor( vdom ) {
    this._currentElement = vdom;
  }
  mountComponent() {
    var result;
    var props = this._currentElement.props;
    if ( this._currentElement.type === "div" ) {
      result = document.createElement( "div" );
      
      for(var key in props ) {
        result.setAttribute( key, props[ key ] );
      }
    } else {
      // 其他類型
    }
    // 迭代子節(jié)點(diǎn)
    props.children.forEach( child=>{
      var childRenderedComponent =  = instantiateReactComponent( child );
      var childMarkup = childRenderedComponent.mountComponent();
      result.appendChild( childMarkup );
    } )
    return result;
  }
}

我們?cè)倏聪?React component 的渲染對(duì)象 ReactCompositeComponentWrapper(主要實(shí)現(xiàn)在 ReactCompositeComponent 里面,ReactCompositeComponentWrapper 只是一個(gè)防止循環(huán)引用的 wrapper

// 以下是偽代碼
class ReactCompositeComponent {
  _currentElement: vdom,
  _rootNodeID: 0,
  _compositeType:
  _instance: 
  _hostParent:
  _hostContainerInfo: 
  // See ReactUpdateQueue
  _updateBatchNumber:
  _pendingElement:
  _pendingStateQueue:
  _pendingReplaceState:
  _pendingForceUpdate:
  _renderedNodeType:
  _renderedComponent:
  _context:
  _mountOrder:
  _topLevelWrapper:
  // See ReactUpdates and ReactUpdateQueue.
  _pendingCallbacks:
  // ComponentWillUnmount shall only be called once
  _calledComponentWillUnmount:

  // render to dom node
  mountComponent( transaction, hostParent, hostContainerInfo, context ) {
    // ---------- 初始化 React.Component --------------
    var Component = this._currentElement.type;
    var publicProps = this._currentElement.props;
    /*
      React.Component 組件有2種:
      new Component(publicProps, publicContext, updateQueue);
      new StatelessComponent(Component);
      對(duì)應(yīng)的 compositeType 有三種
      this._compositeType = StatelessFunctional | PureClass | ImpureClass,
      組件種類和 compositeType 在源碼中都有區(qū)分,但是這里為了簡(jiǎn)單,只示例最常用的一種組件的代碼
    */
    var inst = new Component(publicProps, publicContext, updateQueue);
    
    inst.props = publicProps;
    inst.context = publicContext;
    inst.refs = emptyObject;
    inst.updater = updateQueue;
    
    // 渲染對(duì)象存儲(chǔ)組件對(duì)象
    this._instance = inst;

    // 通過(guò) map 又把組件對(duì)象和渲染對(duì)象聯(lián)系起來(lái)
    ReactInstanceMap.set(inst, this);
    /*
      ReactInstanceMap: {
              -----------------------------------------------
              |                                              |
              v                                              |
        React.Component: ReactCompositeComponentWrapper {    |
          _instance:  <-------------------------------------
        }
      }
      這樣雙方都在需要對(duì)方的時(shí)候可以獲得彼此的引用
    */

    // ---------- 生成 React.Component 的 dom  --------------
    // 組件生命周期函數(shù) componentWillMount 被調(diào)用
    inst.componentWillMount();
    // 調(diào)用 render 方法返回組件的虛擬 dom
    var renderedElement = inst.render();
    // save nodeType  
    var nodeType = ReactNodeTypes.getType(renderedElement);
    this._renderedNodeType = nodeType;
    // 根據(jù)組件的虛擬 dom 生成渲染對(duì)象
    var child = instantiateReactComponent(renderedElement)
    this._renderedComponent = child;
    // 生成真正的 dom node
    // 其實(shí)源碼中的真正代碼應(yīng)該是 var markup = ReactReconciler.mountComponent( child, ... ),
    // 這里為了簡(jiǎn)化說(shuō)明,先不深究 ReactReconciler.mountComponent 還做了點(diǎn)什么
    var markup = child.mountComponent(); 
    // 把組件生命周期函數(shù) componentDidMount 注冊(cè)到回調(diào)函數(shù)中,當(dāng)整個(gè) dom node tree 被添加到容器節(jié)點(diǎn)后觸發(fā)。
    transaction.getReactMountReady().enqueue(inst.componentDidMount, inst);
    return markup;
  }
}
// static member
ReactCompositeComponentWrapper._instantiateReactComponent = instantiateReactComponent

最終的過(guò)程是:

CLICK ME

??|
babel and React.createElement
??|
??v

{
  type: "div",
  props: {
    className: "xxx",
    children: [ {
      type: "span",
      props: {
        children:
      },
      ref:
      key:
    }, {
      type: Form,
      props: {
        children:
      },
      ref:
      key:
    } ] | Element
  }
  ref: "xxx",
  key: "xxx"
}

??|
var domNode = new ReactDOMComponent( vdom ).mountComponent();
??|
??v

domNode = {
  ReactDOMComponent -> 
props: { children: [ ReactDOMComponent -> ReactCompositeComponentWrapper.render() -> vdom -> instantiateReactComponent(vdom) -> ] } }

??|
??|
??v
containerDomNode.appendChild( domNode );

以上是 React 渲染 dom 的一個(gè)基本流程,下一篇計(jì)劃總結(jié)下更新 dom 的流程,即 setState 后發(fā)生了什么。

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

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

相關(guān)文章

  • 2、React組件的生命周期

    摘要:組件生命周期嚴(yán)格定義了組件的生命周期,生命周期可能會(huì)經(jīng)歷如下三個(gè)過(guò)程裝載過(guò)程也就是把組件第一次在樹(shù)上渲染的過(guò)程更新過(guò)程當(dāng)組件被從新渲染的過(guò)程卸載過(guò)程組件從樹(shù)中刪除的過(guò)程。三種不同的過(guò)程,庫(kù)會(huì)調(diào)用組件的一些成員函數(shù),即生命周期函數(shù)。 3. 組件生命周期 React嚴(yán)格定義了組件的生命周期,生命周期可能會(huì)經(jīng)歷如下三個(gè)過(guò)程: 裝載過(guò)程(Mount):也就是把組件第一次在DOM樹(shù)上渲染的...

    Jensen 評(píng)論0 收藏0
  • 4、React組件之性能優(yōu)化

    摘要:組件的性能優(yōu)化高德納我們應(yīng)該忘記忽略很小的性能優(yōu)化,可以說(shuō)的情況下,過(guò)早的優(yōu)化是萬(wàn)惡之源,而我們應(yīng)該關(guān)心對(duì)性能影響最關(guān)鍵的另外的代碼。對(duì)多個(gè)組件的性能優(yōu)化當(dāng)一個(gè)組件被裝載更新和卸載時(shí),組件的一序列生命周期函數(shù)會(huì)被調(diào)用。 React組件的性能優(yōu)化 高德納: 我們應(yīng)該忘記忽略很小的性能優(yōu)化,可以說(shuō)97%的情況下,過(guò)早的優(yōu)化是萬(wàn)惡之源,而我們應(yīng)該關(guān)心對(duì)性能影響最關(guān)鍵的另外3%的代碼。...

    陳偉 評(píng)論0 收藏0
  • React系列---React(三)組件的生命周期

    摘要:組件裝載過(guò)程裝載過(guò)程依次調(diào)用的生命周期函數(shù)中每個(gè)類的構(gòu)造函數(shù),創(chuàng)造一個(gè)組件實(shí)例,當(dāng)然會(huì)調(diào)用對(duì)應(yīng)的構(gòu)造函數(shù)。組件需要構(gòu)造函數(shù),是為了以下目的初始化,因?yàn)樯芷谥腥魏魏瘮?shù)都有可能訪問(wèn),構(gòu)造函數(shù)是初始化的理想場(chǎng)所綁定成員函數(shù)的環(huán)境。 React系列---React(一)初識(shí)ReactReact系列---React(二)組件的prop和stateReact系列---之React(三)組件的生...

    geekzhou 評(píng)論0 收藏0
  • 全面了解 React 新功能: Suspense 和 Hooks

    摘要:他們的應(yīng)用是比較復(fù)雜的,組件樹(shù)也是非常龐大,假設(shè)有一千個(gè)組件要渲染,每個(gè)耗費(fèi)一千個(gè)就是由于是單線程的,這里都在努力的干活,一旦開(kāi)始,中間就不會(huì)停。 悄悄的, React v16.7 發(fā)布了。 React v16.7: No, This Is Not The One With Hooks. showImg(https://segmentfault.com/img/bVblq9L?w=97...

    Baaaan 評(píng)論0 收藏0
  • React Fiber 架構(gòu)理解

    摘要:架構(gòu)理解引用原文是核心算法正在進(jìn)行的重新實(shí)現(xiàn)。構(gòu)建的過(guò)程就是的過(guò)程,通過(guò)來(lái)調(diào)度執(zhí)行一組任務(wù),每完成一個(gè)任務(wù)后回來(lái)看看有沒(méi)有插隊(duì)的更緊急的,把時(shí)間控制權(quán)交還給主線程,直到下一次回調(diào)再繼續(xù)構(gòu)建。 React Fiber 架構(gòu)理解 引用原文:React Fiber ArchitectureReact Fiber is an ongoing reimplementation of Reacts...

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

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

0條評(píng)論

andong777

|高級(jí)講師

TA的文章

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