React 衬着历程

  1. 顺序假设有以下 jsx

class Form extends React.Component {
  constructor() {
    super();
  }
  render() {
    return (
        <form>
          <input type="text"/>
        </form>
    );
  }
}

ReactDOM.render( (
  <div className="test">
    <span onClick={function(){}}>CLICK ME</span>
    <Form/>
  </div>
), document.getElementById('main'))
  1. 拿 ReactDOM render 的部份(<div className="test">...</div>)为例,用 babel 把 jsx 转成 js 后获得以下代码:

React.createElement( 'div', {
  className: 'test'
  },
  React.createElement( 'span',
    { onClick: function(){} },
    'CLICK ME'
  ),
  React.createElement(Form, null)
)
  1. 这里看下 API: React.createElement(component, props, …children)。它天生一个 js 的对象,这个对象是用来代表一个实在的 dom node,这个 js 的对象就是我们俗称的假造dom。(假造 dom 的意义是用 js 对象组织模拟出 html 中 dom 组织,批量的增编削查先直接操纵 js 对象,末了更新到真正的 dom 树上。由于直接操纵 js 对象的速率要比操纵 dom 的那些 api 要快。) 比如根元素 <div className="test"></div> 天生对应出来的假造 dom 是:

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

除了一些 dom 相干的属性,假造 dom 对象还包含些 React 本身须要的属性,比如:ref,key。终究示例中的 jsx 天生出来的假造 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'
}
  1. 有了假造 dom,接下来的事情就是把这个假造 dom 树真正衬着成一个 dom 树。React 的做法是针对差别的 type 组织响应的衬着对象,衬着对象供应一个 mountComponent 要领(担任把对应的某个假造 dom 的节点天生成详细的 dom node),然后轮回迭代全部 vdom tree 天生一个完全的 dom node tree,终究插进去容器节点。检察源码你会发明以下代码:

// vdom 是第3步天生出来的假造 dom 对象
var renderedComponent = instantiateReactComponent( vdom );
// dom node
var markup = renderedComponent.mountComponent();
// 把天生的 dom node 插进去到容器 node 内里,真正在页面上显示出来
// 下面是伪代码,React 的 dom 操纵封装在 DOMLazyTree 内里
containerNode.appendChild( markup );

instantiateReactComponent 传入的是假造 dom 节点,这个要领做的就是依据差别的 type 挪用以下要领天生衬着对象:

// 假如节点是字符串或许数字
return ReactHostComponent.createInstanceForText( vdom(string|number) );
// 假如节点是宿主内置节点,比如浏览器的 html 的节点
return ReactHostComponent.createInternalComponent( vdom );
// 假如是 React component 节点
return new ReactCompositeComponentWrapper( vdom );

ReactHostComponent.createXXX 也只是一层笼统,不是终究的的衬着对象,这层笼统屏障了宿主。比如手机端(React native)和浏览器中一样挪用 ReactHostComponent.createInternalComponent( vdom ); 他天生的终究的衬着对象是差别的,我们当前只议论浏览器环境。字符串和数字没有什么牵挂,在这里我们就不深入探讨了,再进一步看,div 等 html 的原生 dom 节点对应的衬着对象是 ReactDOMComponent 的实例。怎样把 { type:’div’, … } 天生一个 dom node 就在这个类(的 mountComponent 要领)内里。(对怎样天生 div、span、input、select 等 dom node 感兴趣的能够去探讨 ReactDOMComponent,这里不做详细的议论,本文只是想总结下 React 全部衬着历程。下面只给出一个最简示例代码:

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 {
      // 其他范例
    }
    // 迭代子节点
    props.children.forEach( child=>{
      var childRenderedComponent =  = instantiateReactComponent( child );
      var childMarkup = childRenderedComponent.mountComponent();
      result.appendChild( childMarkup );
    } )
    return result;
  }
}

我们再看下 React component 的衬着对象 ReactCompositeComponentWrapper(重要实如今 ReactCompositeComponent 内里,ReactCompositeComponentWrapper 只是一个防备轮回援用的 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);
      对应的 compositeType 有三种
      this._compositeType = StatelessFunctional | PureClass | ImpureClass,
      组件品种和 compositeType 在源码中都有辨别,然则这里为了简朴,只示例最经常使用的一种组件的代码
    */
    var inst = new Component(publicProps, publicContext, updateQueue);
    
    inst.props = publicProps;
    inst.context = publicContext;
    inst.refs = emptyObject;
    inst.updater = updateQueue;
    
    // 衬着对象存储组件对象
    this._instance = inst;

    // 经由过程 map 又把组件对象和衬着对象联系起来
    ReactInstanceMap.set(inst, this);
    /*
      ReactInstanceMap: {
              -----------------------------------------------
              |                                              |
              v                                              |
        React.Component: ReactCompositeComponentWrapper {    |
          _instance:  <-------------------------------------
        }
      }
      如许两边都在须要对方的时刻能够获得相互的援用
    */

    // ---------- 天生 React.Component 的 dom  --------------
    // 组件性命周期函数 componentWillMount 被挪用
    inst.componentWillMount();
    // 挪用 render 要领返回组件的假造 dom
    var renderedElement = inst.render();
    // save nodeType  
    var nodeType = ReactNodeTypes.getType(renderedElement);
    this._renderedNodeType = nodeType;
    // 依据组件的假造 dom 天生衬着对象
    var child = instantiateReactComponent(renderedElement)
    this._renderedComponent = child;
    // 天生真正的 dom node
    // 实在源码中的真正代码应该是 var markup = ReactReconciler.mountComponent( child, ... ),
    // 这里为了简化申明,先不穷究 ReactReconciler.mountComponent 还做了点什么
    var markup = child.mountComponent(); 
    // 把组件性命周期函数 componentDidMount 注册到回调函数中,当全部 dom node tree 被添加到容器节点后触发。
    transaction.getReactMountReady().enqueue(inst.componentDidMount, inst);
    return markup;
  }
}
// static member
ReactCompositeComponentWrapper._instantiateReactComponent = instantiateReactComponent
  1. 终究的历程是:

<div className="test">
  <span onClick={this.click}>CLICK ME</span>
  <Form/>
</div>

  |
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 -> <div/>
  props: {
    children: [
      ReactDOMComponent -> <span/>
      ReactCompositeComponentWrapper.render() -> vdom -> instantiateReactComponent(vdom) -> <form><input/></from>
    ]
  }
}

  |
  |
  v
containerDomNode.appendChild( domNode );

以上是 React 衬着 dom 的一个基础流程,下一篇设计总结下更新 dom 的流程,即 setState 后发生了什么。

    原文作者:Bernie维尼
    原文地址: https://segmentfault.com/a/1190000009415798
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞