每次都信誓旦旦的给本身立下要好好学习react源码的flag,结果都是由于某个地方卡住了,或是其余缘由没看多少就放弃了。此次又给本身立个flag-坚持看完react
源码。为了敦促本身,特开设这样一个专栏来记录本身的学习历程,这意味着这个专栏的文章质量并不高,你能够拿来参考参考,切莫全信,我不想误人子弟,后面要是学有所成再考虑产出些好点的文章。 要是发现文章中有什么不当之处,欢迎批评交流。我看的源码版本是16.8.2
。我是用在源码加注释的方法学习的,放在 github上。
为了看react源码,我查找了很多资料,这里推荐两个参考资料,我的以为写得不错。javascript
15.6.2
的, 在react16
里面一些方法找不到了。Component, PureComponent是咱们最经常使用的东西,咱们常常继承他们来建立组件。所以,我选择从这几个最最经常使用的东西入手开始欣赏React源码。他们都位于packages/react目录下,入口在index.js,index.js里边导出的实际上是src下的React.js里的东西,在React.js中能够看到React暴露的API。在React.js中能够找到上面说述的Component,PureComponent和ReactElement相关线索。html
Component和PureComponent都位于/packages/react/src/ReactBaseClasses.js。java
这两个东西都是构造函数,或者称为类。react
Component的构造函数长成以下这样:git
/** * Base class helpers for the updating state of a component. */ // 常常去继承他,原来这个构造行数是这样的 function Component(props, context, updater) { this.props = props; this.context = context; // If a component has string refs, we will assign a different object later. this.refs = emptyObject; // We initialize the default updater but the real one gets injected by the // renderer. // 这个new的时候须要注意updater是哪里来的, 这个updater与setState应该有很大关系 this.updater = updater || ReactNoopUpdateQueue; }
这并无什么神奇的,他接收三个参数,挂到this上。具体是这三个参数是啥,我目前也是不清楚的,由于咱们平时使用都是extends他而并无new他,new的过程应该是框架去作的,这个获得后面再作分析。后面分析时须要注意updater,感受这里会是一个重点,他有一个默认值,ReactNoopUpdateQueue,去看了下他的代码,他是一个对象,挂了一些方法,这里也就不展开了,我也没太细看。github
Component的原型上挂了一些方法和属性,isReactComponent属性,setState方法,forceUpdate方法,代码以下:api
// 一般isXxx都是boolean类型的,这里比较奇怪,后面须要关注下 Component.prototype.isReactComponent = {}; /** * ...这里有不少说明,能够直接去看 * * @param {object|function} partialState Next partial state or function to * produce next partial state to be merged with current state. * @param {?function} callback Called after state is updated. * @final * @protected */ // 原来咱们平时调用的setState就这么几行啊,可是看他是调用的updater的enqueueSetState, // 相关实现应该在那里边了, 能够updater这个东西很厉害 Component.prototype.setState = function(partialState, callback) { // 这里是个参数校验,校验不经过的话会给提示信息,并抛出异常 invariant( typeof partialState === 'object' || typeof partialState === 'function' || partialState == null, 'setState(...): takes an object of state variables to update or a ' + 'function which returns an object of state variables.', ); this.updater.enqueueSetState(this, partialState, callback, 'setState'); }; /** * ...这里有不少说明,能够直接去看 * * @param {?function} callback Called after update is complete. * @final * @protected */ // 不多用到这个方法啊, 但他和setState同样都是Component原型上的方法 Component.prototype.forceUpdate = function(callback) { this.updater.enqueueForceUpdate(this, callback, 'forceUpdate'); };
其实Component的原型上挂载的东西也没什么神奇的,其中很是重要的是updater的enqueueSetState,enqueueForceUpdate方法,进一步说明了updater是后面分析的重点。框架
接下来的一段代码是用来在开发模式下标记废弃的api的,在开发模式下回给写提示,代码以下:函数
// 这里是标识一些废弃的api, 开发模式会报出来提醒开发这注意 if (__DEV__) { const deprecatedAPIs = { isMounted: [ 'isMounted', 'Instead, make sure to clean up subscriptions and pending requests in ' + 'componentWillUnmount to prevent memory leaks.', ], replaceState: [ 'replaceState', 'Refactor your code to use setState instead (see ' + 'https://github.com/facebook/react/issues/3236).', ], }; const defineDeprecationWarning = function(methodName, info) { Object.defineProperty(Component.prototype, methodName, { get: function() { lowPriorityWarning( false, '%s(...) is deprecated in plain JavaScript React classes. %s', info[0], info[1], ); return undefined; }, }); }; for (const fnName in deprecatedAPIs) { if (deprecatedAPIs.hasOwnProperty(fnName)) { defineDeprecationWarning(fnName, deprecatedAPIs[fnName]); } } }
__DEV__这个东西我没找到是在哪里挂到全局的(知道的同窗能够留言指点下),可是看变量名能够推测他是开发模式标识,这个提示咱们在作一些给别人用的东西时,接口协议约定十分重要,一旦约定就不能轻易变动,确实须要变动时须要通知调用方调整。回头来,这里标识废弃了isMounted,replaceState两个方法,其实他们被挪到了updater里边。oop
开始用React时老大Rewview个人代码时常常写评论,“你这个Component能够改为PureComponent”,当时一直不懂PureComponent与Component的区别(如今也没全懂),只是听人说PureComponent更新的时候是浅比较,而Component是深比较。今天看了这部分,其实也没懂,不过感受后面再看看应该就懂了。要搞清这里的PureComponet须要了解下js中继承的实现,你们能够参考《JavaScript高级程序设计》相关介绍,也能够看看理解js继承的6种方式, 笔者看到这个PureComponet也是先复习了下才看的。无论你看没看, 代码先贴出来:
// PureComponent function ComponentDummy() {} ComponentDummy.prototype = Component.prototype; // 发现PureComponnet的构造方法和Component是相同的 /** * Convenience component with default shallow equality check for sCU. */ function PureComponent(props, context, updater) { this.props = props; this.context = context; // If a component has string refs, we will assign a different object later. this.refs = emptyObject; this.updater = updater || ReactNoopUpdateQueue; } const pureComponentPrototype = (PureComponent.prototype = new ComponentDummy()); pureComponentPrototype.constructor = PureComponent; // Avoid an extra prototype jump for these methods. // 感受不用加也能够, 只不过会多查找一次,可是不得不说细节考虑的真棒 Object.assign(pureComponentPrototype, Component.prototype); pureComponentPrototype.isPureReactComponent = true;
我画了个图来理解这个继承。
首先是建立了一个ComponentDummy构造函数,他的原型指到Component的原型;而后建立了一个PureComponent, 加上了和Component同样的属性(这里为啥不用call)。PureComponent的原型指向ComponentDummy的实例;修改PureComponent原型的constructor属性使其正确指向PureComponent的构造函数,并挂一个isPureReactComponent的属性。为了减小向上去查找原型链次数,用了一个assign直接将Component原型的东西拷贝到PureComponent的原型上(这里仍是考虑的比较精细的)。
首先这个实现没有啥问题,可是我有个疑问,你们能够留言指点下:
为何要用继承,注意到PureComponent的构造函数和Component是同样的,而后还有一个拷贝Component的原型到PureComponent的原型的操做,那这里有继承的必要吗?不都是重写的吗,感受画蛇添足。
下一篇预告 ReactElement源码解析