首页 > 代码库 > 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; }};
通过上面的代码我们可以知道:
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; } } } }}
通过以上代码就可以大致了解其工作原理了
而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); }};
定义了 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‘); }};
2.ReactComponent
ReactComponent的原型请参见上面的代码,其构造函数如下
function ReactComponent(props, context, updater) { this.props = props; this.context = context; this.refs = emptyObject; this.updater = updater || ReactNoopUpdateQueue;}
对于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 源码简读【未完待续】