首页 > 代码库 > React v16-alpha 源码简读【未完待续】

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 源码简读【未完待续】