React v16-alpha 源码简读未完待续

Posted 白菜帮子

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了React v16-alpha 源码简读未完待续相关的知识,希望对你有一定的参考价值。

一、物料准备

1.克隆react源码, github 地址:https://github.com/facebook/react.git

2.安装gulp  

3.在react源码根目录下:

   $npm install

   $gulp default

   (建议使用node 6.0+)

  gulp将文件处理在根目录下的build文件夹中,打开build查看react的源码,结构清晰,引用路径明了

二、从生成 virtual dom 开始

react 生成一个组件有多种写法:

es 5下:var Cp=React.createClass({...})

es 6下:class Cp extends React.Component{...}

下面打开./build/node_modules/react/lib 文件夹,找到React.js 可以看到如下关键代码:

var React = {

  // Modern

  Children: {
    map: ReactChildren.map,
    forEach: ReactChildren.forEach,
    count: ReactChildren.count,
    toArray: ReactChildren.toArray,
    only: onlyChild
  },

  Component: ReactComponent,
  PureComponent: ReactPureComponent,

  createElement: createElement,
  cloneElement: cloneElement,
  isValidElement: ReactElement.isValidElement,

  // Classic

  PropTypes: ReactPropTypes,
  createClass: ReactClass.createClass,
  createFactory: createFactory,
  createMixin: function (mixin) {
    // Currently a noop. Will be used to validate and trace mixins.
    return mixin;
  },

  // This looks DOM specific but these are actually isomorphic helpers
  // since they are just generating DOM strings.
  DOM: ReactDOMFactories,

  version: ReactVersion,

  // Deprecated hook for JSX spread, don‘t use this for anything.
  __spread: __spread
};

由此得知:React.createClass => ReactClass.createClass    

              React.component => ReactComponent

1.ReactClass.createClass

下面还是在当前的目录下寻找ReactClass.js文件,查看到如下关键代码段:

技术分享
var ReactClass = {
    createClass: function (spec) {
    var Constructor = function (props, context, updater) {
       //如果不是生产环境 输出信息类警告 目前忽略
      if (process.env.NODE_ENV !== ‘production‘) {...}
       // 自动绑定相关方法 目前忽略
      if (this.__reactAutoBindPairs.length) {...}
       //为组件绑定props  context refs updater属性
      this.props = props;
      this.context = context;
      this.refs = emptyObject;
      this.updater = updater || ReactNoopUpdateQueue;
      //初始组件state为null
      this.state = null;
      //如果有getInitialState则执行
      var initialState = this.getInitialState ? this.getInitialState() : null;
      //在非生产环境下为配合mock 设置initialState为null 目前忽略
      if (process.env.NODE_ENV !== ‘production‘) {...}
      //其他情况下的兼容性处理,目前忽略
      ...
      //将初始化的state赋值给组件state
      this.state = initialState;
    };
    //设置Constructor的原型
    Constructor.prototype = new ReactClassComponent();
    Constructor.prototype.constructor = Constructor;
    Constructor.prototype.__reactAutoBindPairs = [];
    //合并研发同学写入的createClass({中的东西})
    mixSpecIntoComponent(Constructor, spec);
    //如果存在getDefaultProps则执行
    if (Constructor.getDefaultProps) {
      Constructor.defaultProps = Constructor.getDefaultProps();
    }
   ...省略一些无关主逻辑的操作

    return Constructor;
  }

};
View Code

通过上面的代码我们可以知道:

a.createClass生成一个constructor并return它,这个constructor就是我们的组件
b.这个constructor继承自ReactClassComponent
c.了解react组件声明周期的同学应该知道React组件在整个生命周期中getDefaultProps只执行一次了吧
d.研发组件的同学在createClass({中写的东西})是通过mixSpecIntoComponent方法融合进constructor中的

下面请看mixSpecIntoComponent代码

技术分享
function mixSpecIntoComponent(Constructor, spec) {
  if (!spec) {
    //当spec不存在时 即研发同学没有写createClass中的东西
    ...省略警告文本
    return;
  }
  ...省略spec类型容错处理
  var proto = Constructor.prototype;
  var autoBindPairs = proto.__reactAutoBindPairs;
  //关于mixins的相关处理 其实就是递归调用mixSpecIntoComponent
  //MIXINS_KEY="mixins"
  if (spec.hasOwnProperty(MIXINS_KEY)) {
    RESERVED_SPEC_KEYS.mixins(Constructor, spec.mixins);
  }
  //循环遍历spec
  for (var name in spec) {
    ...省略容错处理
    var property = spec[name];
    var isAlreadyDefined = proto.hasOwnProperty(name);
    //覆写constructor.prototype中的方法
    validateMethodOverride(isAlreadyDefined, name);
    //对特定的属性名做特殊处理
    if (RESERVED_SPEC_KEYS.hasOwnProperty(name)) {
      RESERVED_SPEC_KEYS[name](Constructor, property);
    } else {
      ...省略特殊处理
      if (shouldAutoBind) {
        ...省略自动绑定相关处理
      } else {
        if (isAlreadyDefined) {
           ...省略已定义容错处理
        } else {
         //关键点  将property赋值给Contructor
          proto[name] = property;
         
        }
      }
    }
  }
}
View Code

通过以上代码就可以大致了解其工作原理了

而ReactClassComponent函数生成代码如下:

var ReactClassComponent = function () {};
_assign(ReactClassComponent.prototype, ReactComponent.prototype, ReactClassMixin);

它的原型是由ReactComponent.prototype及ReactClassMixin复合而成(_assing在根目录 node_modules/fbjs目录下,为facebook工具库中封装的函数,相当于es6 的 Object.assign)

ReactClassMixin源码如下:

技术分享
var ReactClassMixin = {
  replaceState: function (newState, callback) {
    this.updater.enqueueReplaceState(this, newState);
    if (callback) {
      this.updater.enqueueCallback(this, callback, ‘replaceState‘);
    }
  },
  isMounted: function () {
    return this.updater.isMounted(this);
  }
};
View Code

定义了 replaceState及 isMounted两个方法

至于ReactComponent在./ReactComponent.js文件中,prototype源码如下

技术分享
ReactComponent.prototype.isReactComponent = {};

//setState方法
ReactComponent.prototype.setState = function (partialState, callback) {
  ...省略报警信息
  this.updater.enqueueSetState(this, partialState);
  if (callback) {
    this.updater.enqueueCallback(this, callback, ‘setState‘);
  }
};

ReactComponent.prototype.forceUpdate = function (callback) {
  this.updater.enqueueForceUpdate(this);
  if (callback) {
    this.updater.enqueueCallback(this, callback, ‘forceUpdate‘);
  }
};
View Code

 

2.ReactComponent

ReactComponent的原型请参见上面的代码,其构造函数如下

技术分享
function ReactComponent(props, context, updater) {
  this.props = props;
  this.context = context;
  this.refs = emptyObject;
  this.updater = updater || ReactNoopUpdateQueue;
}
View Code

对于extends 关键字的使用,可以参看babel上对于extends的转换,以了解其运行机制
简单点说,extends转换成ES5有以下两个步骤:

1.Object.create方法去生成对象,即:Cp.prototype=Object.create(ReactComponent.prototpe,{配置对象}) 实现原型继承的目的

2.通过ReactComponent.apply(this,arguments)的方法实现构造函数的继承

实际转换加上属性的验证十分繁杂,有兴趣的同学请亲自实践

这种通过extends方式生成的组件,没有createClass中对getInitialState及getDefaultProps的显示管理

需要研发同学在constructor中进行处理,至于其背后有何机制,以后再做讨论  

三、将virtual dom变成 dom

【未完待续】

以上是关于React v16-alpha 源码简读未完待续的主要内容,如果未能解决你的问题,请参考以下文章

ArrayList 源码分析(未完待续)

源码分析之ArrayList(未完待续,写到了274行)

软件测试2小时入门(未完待续)

未完待续Java蓝桥杯--算法训练典型问题的递归框架

itop-4412开发板使用第一篇-信号量的学习使用(未完待续)

MYSQL原生态SQL语句(未完待续)