react源代码重点难点分析 (2)

function batchedMountComponentIntoNode(componentInstance, container, shouldReuseMarkup, context) { //从根元素provider开始
  //batchedMountComponentIntoNode以transaction事务的形式调用mountComponentIntoNode
  transaction.perform(mountComponentIntoNode, null, componentInstance, container, transaction, shouldReuseMarkup, context);

    function mountComponentIntoNode(wrapperInstance, container, transaction, shouldReu //从根元素provider开始
      var markup = ReactReconciler.mountComponent(wrapperInstance, transaction, //mountComponent相当于compile,从根节点开始编译
      //Reconciler.mountComponent就是执行instance里面的mountComponent,在执行performInitialMount时会递归调用自己。
      //mountComponent的目的是解析得到每个节点的HTML代码(最后插入网页生效),react叫做markup,是类似vue的vnode对象。

        Reconciler.mountComponent: function (internalInstance, transaction, 
          var markup = internalInstance.mountComponent(transaction, hostParent, hostContainerInfo, context, parentDebugID);
          //对于provider这样的控制组件标签来说,产生的html代码就是一个不可见的comment文本
          return markup;
          // internalInstance是不同的component实例,最典型的component节点类型是html元素节点比如<div>和组件元素节点比如<App>

        Reconciler.mountComponent会递归调用自己完成从根元素递归到最底层元素<div>,是react源码的最核心最关键最牛的代码,因为前端代码要递归html元素tree,这是与后台代码不同的,也是非常复杂的,一旦递归元素tree,就要开始晕菜了,但代码效率巨高,一个递归就完事了,递归也是最体现程序牛的地方,人工智能自我学习肯定也是要用程序递归技术的。

 

不同的instance有不同的mountComponent方法,我们先来看组件元素的mountComponent方法:

mountComponent: function (transaction, hostParent, hostContainerInfo, context) { //mountComponent其实就是编译节点的意思,react把一切节点视为component
  var publicProps = this._currentElement.props;
  var inst = this._constructComponent(doConstruct, publicProps, publicContext, updateQueue);
  //inst就是new组件实例,那么是在mountComponent阶段初始化组件实例的,new组件实例之后,执行其render()方法就又产生Element,就又需要递归循环element -> instance -> instance.mountComponent -> inst(组件实例) -> render() -> element 如此递归到子节点
    _constructComponent: function (doConstruct, publicProps, publicContext, updateQueue) {
      return this._constructComponentWithoutOwner(doConstruct, publicProps, publicContext, updateQueue);

        _constructComponentWithoutOwner: function (doConstruct, publicProps, publicContext, updateQueue) {
          var Component = this._currentElement.type; //type就是组件构造函数(组件定义代码)
          return new Component(publicProps, publicContext, updateQueue); //App组件外套connect组件,这就是new Connect()组件实例的位置,找到这个位置在分析react源码的道路上就前进了一大步,因为组件定义代码无外乎就是定义一些属性,框架肯定准备了一个组件基类,到时一合并,再new实例,这是js唯一的机制,不可能有其它方法,找到这个位置,再前后去追踪,就能看懂框架到底是如何初始化组件实例的,我们定义的组件代码到底到底是如何被执行的。

  inst.props = publicProps; // double set this.props
  this._instance = inst; //new组件实例保存在instance中,只要执行instance的方法,就可以从this取回inst实例,再执行inst实例里面的render()方法产生一个Element递归下去
  markup = this.performInitialMount(renderedElement, hostParent, hostContainerInfo, transaction, context);
  //inst保存在this实例中,调用performInitialMount时无需传递inst,renderedElement是空的
  return markup; //markup就是编译产生的结果,相当于vnode,含html代码

    performInitialMount: function (renderedElement, hostParent, hostContainerInfo, transaction, context) {

      renderedElement = this._renderValidatedComponent(); //执行inst.render()产生Element,每种组件inst有自己的render方法,provder/router/connect/app组件都有自己的render方法,app的render方法是应用写的,系统组件的render方法都是事先设计好的,比如connect的render方法,还有一个router-context组件
//app的render方法里面是jsx语法,编译时每个节点已经转换为createElement(),所以render方法就是返回一个根元素Element,它里面有多少子元素再递归处理

      var child = this._instantiateReactComponent(renderedElement,    //根据元素类型生成instance
      this._renderedComponent = child;
      var markup = ReactReconciler.mountComponent(child, // 在这里递归调用Reconciler.mountComponent,处理下一个子节点child,是前面根据Element生成的
      return markup;

 

这是组件component的mountComponet编译方法,再来看html元素component的mountComponent编译方法:

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zyzzxz.html