react源码解读 {createClass}

对一个框架源码的解读,既有利于更深入地了解框架,使用上更得心应手,又可以学习到其中代码组织的思路,吸收其精华简洁的写法以便于日常工作上使用。下面我就挑选近年大热门react(15.3.1),从中剖析框架的设计思路,由浅入深地学习。

我们从这个文件开始看起,这是react的主入口(./lib/react.js)。

/*** Copyright 2013-present, Facebook, Inc.* All rights reserved.** This source code is licensed under the BSD-style license found in the* LICENSE file in the root directory of this source tree. An additional grant* of patent rights can be found in the PATENTS file in the same directory.** @providesModule React*/'use strict';var _assign = require('object-assign');var ReactChildren = require('./ReactChildren');
var ReactComponent = require('./ReactComponent');
var ReactPureComponent = require('./ReactPureComponent');
var ReactClass = require('./ReactClass');
var ReactDOMFactories = require('./ReactDOMFactories');
var ReactElement = require('./ReactElement');
var ReactPropTypes = require('./ReactPropTypes');
var ReactVersion = require('./ReactVersion');var onlyChild = require('./onlyChild');
var warning = require('fbjs/lib/warning');var createElement = ReactElement.createElement;
var createFactory = ReactElement.createFactory;
var cloneElement = ReactElement.cloneElement;if (process.env.NODE_ENV !== 'production') {var ReactElementValidator = require('./ReactElementValidator');createElement = ReactElementValidator.createElement;createFactory = ReactElementValidator.createFactory;cloneElement = ReactElementValidator.cloneElement;
}var __spread = _assign;if (process.env.NODE_ENV !== 'production') {var warned = false;__spread = function () {process.env.NODE_ENV !== 'production' ? warning(warned, 'React.__spread is deprecated and should not be used. Use ' + 'Object.assign directly or another helper function with similar ' + 'semantics. You may be seeing this warning due to your compiler. ' + 'See https://fb.me/react-spread-deprecation for more details.') : void 0;warned = true;return _assign.apply(null, arguments);};
}var React = {// ModernChildren: {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,// ClassicPropTypes: 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
};module.exports = React;

我们直接跳过前面的环境判断以及模块引入,可以看到从50行起就是React的关键代码。并且我们可以清晰的从上面看到React所提供的方法。这是离我们使用者最近的一层,看到信息量不多。我们就按照开发的思路,一步一步地深入源码。
编写一个组件,当然是从创建开始,我们使用的是 React.createClass,不难发现,React.createClass实际上引用的是ReactClass.createClass。当然我们也可以用ES6的写法直接继承至React.Component.这两种写法有什么差异存在,我们先把悬念放在后面。
先从createClass的源码看起(./lib/ReactClass)。

var ReactClass = {/*** Creates a composite component class given a class specification.* See https://facebook.github.io/react/docs/top-level-api.html#react.createclass** @param {object} spec Class specification (which must define `render`).* @return {function} Component constructor function.* @public*/createClass: function (spec) {var Constructor = function (props, context, updater) {// This constructor gets overridden by mocks. The argument is used// by mocks to assert on what gets mounted.if (process.env.NODE_ENV !== 'production') {process.env.NODE_ENV !== 'production' ? warning(this instanceof Constructor, 'Something is calling a React component directly. Use a factory or ' + 'JSX instead. See: https://fb.me/react-legacyfactory') : void 0;}// Wire up auto-bindingif (this.__reactAutoBindPairs.length) {bindAutoBindMethods(this);}this.props = props;this.context = context;this.refs = emptyObject;this.updater = updater || ReactNoopUpdateQueue;this.state = null;// ReactClasses doesn't have constructors. Instead, they use the// getInitialState and componentWillMount methods for initialization.var initialState = this.getInitialState ? this.getInitialState() : null;if (process.env.NODE_ENV !== 'production') {// We allow auto-mocks to proceed as if they're returning null.if (initialState === undefined && this.getInitialState._isMockFunction) {// This is probably bad practice. Consider warning here and// deprecating this convenience.initialState = null;}}!(typeof initialState === 'object' && !Array.isArray(initialState)) ? process.env.NODE_ENV !== 'production' ? invariant(false, '%s.getInitialState(): must return an object or null', Constructor.displayName || 'ReactCompositeComponent') : _prodInvariant('82', Constructor.displayName || 'ReactCompositeComponent') : void 0;this.state = initialState;};Constructor.prototype = new ReactClassComponent();Constructor.prototype.constructor = Constructor;Constructor.prototype.__reactAutoBindPairs = [];injectedMixins.forEach(mixSpecIntoComponent.bind(null, Constructor));mixSpecIntoComponent(Constructor, spec);// Initialize the defaultProps property after all mixins have been merged.if (Constructor.getDefaultProps) {Constructor.defaultProps = Constructor.getDefaultProps();}if (process.env.NODE_ENV !== 'production') {// This is a tag to indicate that the use of these method names is ok,// since it's used with createClass. If it's not, then it's likely a// mistake so we'll warn you to use the static property, property// initializer or constructor respectively.if (Constructor.getDefaultProps) {Constructor.getDefaultProps.isReactClassApproved = {};}if (Constructor.prototype.getInitialState) {Constructor.prototype.getInitialState.isReactClassApproved = {};}}!Constructor.prototype.render ? process.env.NODE_ENV !== 'production' ? invariant(false, 'createClass(...): Class specification must implement a `render` method.') : _prodInvariant('83') : void 0;if (process.env.NODE_ENV !== 'production') {process.env.NODE_ENV !== 'production' ? warning(!Constructor.prototype.componentShouldUpdate, '%s has a method called ' + 'componentShouldUpdate(). Did you mean shouldComponentUpdate()? ' + 'The name is phrased as a question because the function is ' + 'expected to return a value.', spec.displayName || 'A component') : void 0;process.env.NODE_ENV !== 'production' ? warning(!Constructor.prototype.componentWillRecieveProps, '%s has a method called ' + 'componentWillRecieveProps(). Did you mean componentWillReceiveProps()?', spec.displayName || 'A component') : void 0;}// Reduce time spent doing lookups by setting these on the prototype.for (var methodName in ReactClassInterface) {if (!Constructor.prototype[methodName]) {Constructor.prototype[methodName] = null;}}return Constructor;},injection: {injectMixin: function (mixin) {injectedMixins.push(mixin);}}};

644行起,createClass方法首先定义了一个Constructor构造函数,折叠内部,我们看看这个方法在返回一个构造函数前做了什么,
直接跳到681行,构造函数的prototype指向一个ReactClassComponent的实例。

   Constructor.prototype = new ReactClassComponent();

往上翻我们可以发现,ReactClassComponent的prototype属性,拷贝了ReactComponent.prototype 和 ReactClassMixin,因此我们的组件可以使用ReactComponent原型上的方法。

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

683行到687行。
定义了 __reactAutoBindPairs 为一个空数组。
先将mixin里面的方法按照key,function内容的顺序成对存入 __reactAutoBindPairs ,
接着就是spec对象里的方法用同样的方式存入。

    Constructor.prototype.__reactAutoBindPairs = [];injectedMixins.forEach(mixSpecIntoComponent.bind(null, Constructor));mixSpecIntoComponent(Constructor, spec);

690行我们可以看到Constructor.defaultProps 就是我们开发中 getDefaultProps()所返回的对象。

    if (Constructor.getDefaultProps) {Constructor.defaultProps = Constructor.getDefaultProps();}

694行 -- 712行 是在开发环境中对开发者的建议,以及规范使用的警示。
715行 -- 719行 可以知道我们创建一个组件需要定义的方法都在ReactClassInterface上有,当前未定义的方法设置为空,我们就可以通过打印组件的prototype属性清楚地在日志上知道我们有哪些api是未定义的。通过设置未定义的属性为空,可以减少程序查找的时间。
721行 最终返回了这个封装好的构造函数。

    for (var methodName in ReactClassInterface) {if (!Constructor.prototype[methodName]) {Constructor.prototype[methodName] = null;}}return Constructor;

看到这里我们可以明白一点,组件实质上是一个构造函数,而我们自定义的方法,既存在了prototype里,也按照[key,content,key,content...]的方式归纳到了Constructor.prototype.__reactAutoBindPairs 里。这是为了组件实例化时可以将这些方法直接遍历绑定在实例上,并且避免了React官方指定的方法也被绑定在实例上。

接下来我们展开645行的Constructor,可以看到实例化的时候主要做了两件事。
654行
第一件事就是将上文提到的存在Constructor.prototype.__reactAutoBindPairs 的内容成对取出,绑定在实例上。

      if (this.__reactAutoBindPairs.length) {bindAutoBindMethods(this);}

668行 ——679行
第二件事就是判断组件是否有定义getInitialState,如果有,则将state设置为该方法返回的值,如果没有设置state为null。

      var initialState = this.getInitialState ? this.getInitialState() : null;if (process.env.NODE_ENV !== 'production') {// We allow auto-mocks to proceed as if they're returning null.if (initialState === undefined && this.getInitialState._isMockFunction) {// This is probably bad practice. Consider warning here and// deprecating this convenience.initialState = null;}}!(typeof initialState === 'object' && !Array.isArray(initialState)) ? process.env.NODE_ENV !== 'production' ? invariant(false, '%s.getInitialState(): must return an object or null', Constructor.displayName || 'ReactCompositeComponent') : _prodInvariant('82', Constructor.displayName || 'ReactCompositeComponent') : void 0;this.state = initialState;

到这里我们大概地知道了一个组件从创建构造函数到实例化的时候做了什么事情了。后续我们继续解读更底层的ReactComponent。

希望能对大家有帮助。
如果有错误的地方,恳请各位大神指正。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/259735.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

mysql分析sql语句性能_sql语句执行性能分析

explain根据上面提到的explain去比较,就可以得出结果了mysql> explain select * from users limit 1000,20;---------------------------------------------------------------------------------| id | select_type | table | type | possible_keys | key | key…

sourceTree添加git密钥步骤

给多个远程服务器比如https://github.com/wangjian2014/wjtest/blob/master/wj.txt添加public密钥 本地服务器添加private密钥 SSH Client 选择PuTTY/Plink 选择Generate,生成public 和private密钥,将public密钥数据复制到远程服务器上面 保存private…

[tomcat] 配置数据源介绍

从tomcat5.5开始,内置了DBCP数据源的实现。tomcat数据源提供两种配置方式,两种数据源的访问范围不同, 1.全局数据源:顾名思义在tomcat应用下的所有web都可以访问。 2.局部数据源:适用单个web应用 ★★ 不管以那种方式都得提供特定数据源的jdbc驱动。 此…

background-size

background-size:contain;contain:包含 按比例调整图片,使得图片的宽度自适应容器的宽度。 相当于在ps中,约束比例设置原始图片的宽度值等于容器的宽度值。 如果图片过大,等比压缩后容器的高度方向上可能会有空白。 background-size:cover;co…

在mybatis用mysql的代码块_关于Mybatis 中使用Mysql存储过程的方法

1.存储过程的简介我们常用的操作数据库语言SQL语句在执行的时候需要要先编译,然后执行,而存储过程(Stored Procedure)是一组为了完成特定功能的SQL语句集,经编译后存储在数据库中,用户通过指定存储过程的名字并给定参数(如果该存储…

MySQL5.6免安装配置与“系统找不到指定的文件”错误

1.下载免安装版本的mysql-5.6.11-winx64 (本机 win7 64位)2.将文件解压到任意,不要有中文(有中文的情况没试过,不过最好避免这种情况)3.配置mysql 环境变量,在 path后面加上D:\Program Files\mysql-5.6.11-winx64\bin…

安装配置OSA运维管理平台

1、下载完整包V1.0.2wget http://www.osapub.com/download/OSA_BETA_V1.0.2.tar.gzV1.0.5wget http://www.osapub.com/download/OSA_BETA_V1.0.5.tar.gz 2、解压安装tar xvf OSA_BETA_V1.0.5.tar.gzmv osa /usr/local/ PS:该版本只允许指向/usr/local/osa/目录&…

as5300g2 nas软件功能_【浪潮混闪存储AS5300G5-可同时提供SAN和NAS两种服务的中端混闪存储系统】价格_厂家 - 中国供应商...

功能特性极速性能(1)平台升级:G5采用全新一代硬件平台,芯片升级、规格升级,性能同比上一代平均提升30%,为提高存储系统的数据处理效率提供有力支撑。同时结合G5的智能软件,如智能缓存加速、智能分层、智能QOS等高级功能…

c 总结

C-总结 #pragma mark - 第一章:C基础 void func1(); void func1() { // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ int a 030; // 以0开头得数是八进制的数,计算的时候要换算成10进制进行计算 int b a * 10; printf("%d", b); // 此时打印…

windows下使用cpanm进行模块安装

windows下使用cpanm进行模块安装要放假了,突然想整理一下手头上的软件,突然发现perl的安装模块这个功能不能用。弄了一下,使得windows 下 perl 的 cpanm能用,避免成天为了依赖痛苦。软件版本:#理论上此方法所有版本通用…

Response缓冲区

1 protected void Page_Load(object sender, EventArgs e)2 {3 //关闭缓冲区,输出会一个一个写出来(只有在火狐浏览器中才有效果)。4 //Response.BufferOutput false;5 6 //开启缓冲区7 Response.Buffe…

Javascript模块模式学习分享

之前一直也有听说和接触到模块模式、这次整理了一下、感觉蛮有收获的、特来分享。 模块模式很基本的一点就是匿名函数的 闭包、通过这点来实现。 1 //模块模式2 3 var MODULE (function(){4 /*函数默认是返回this的、但是定义了my对象后、return my; 返回值就变成了my对象…

Source Insight基本使用和快捷键

为什么要用Source Insight呢?貌似是因为比完整的IDE要更快一些,比较利于查看大量的代码。 软件的安装很简单,设置好安装目录。 配置好文档路径,当然这个也可以在Options里面改,选Options->Preferences…里面的Folde…

powerquery mysql数据库_window 10 下 --excel | power query 通过 ODBC链接 mysql 数据库

excel链接到mysql的方法有几种,今天主要介绍如何通过ODBC链接odbc是 “开放数据库连接”,你可以通过下载插件使得自己的excel可以连接到不同的数据库。关于版本的选择,就是excel版本obdc版本mysql obdc版本(需要一样)第一步 下载mysql odbc…

table样式

一直以来&#xff0c;css和JS都是软肋&#xff0c;因为需要不得不重新温故。 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title><style type"text/css">table.hover…

MAC和XCODE常用快捷键

摘自&#xff1a;http://www.cnblogs.com/yjmyzz/archive/2011/01/25/1944325.html 1. 文件CMD N: 新文件CMD SHIFT N: 新项目CMD O: 打开CMD S: 保存CMD SHIFT S: 另存为CMD W: 关闭窗口CMD SHIFT W: 关闭文件2. 编辑CMD [: 左缩进CMD ]: 右缩进CMD CTRL LEFT: …

数组与内存控制

注&#xff1a;我已对本文章进行了更新&#xff0c;劳烦移步。 java语言是典型的静态语言&#xff0c;因而&#xff0c;数组也是静态的&#xff0c;即当该数组被初始化之后&#xff0c;该数组的长度是不可变的。java 语言的数组变量是引用类型&#xff0c;什么意思呢&#xff1…

NRedis-Proxy - 高性能中间件服务器

2019独角兽企业重金招聘Python工程师标准>>> 高性能中间件服务器 一、 NRedis-Proxy 介绍 NRedis-Proxy 是一个Redis中间件服务&#xff0c;第一个Java 版本开源Redis中间件&#xff0c;无须修改业务应用程序任何代码与配置&#xff0c;与业务解耦&#xff1b;以Spr…

python图片识别验证码软件_python识别图片验证码

http://robertgawron.blogspot.hk/2010/11/almost-all-sites-use-images-with-text.html图片的识别主要有&#xff0c;去色&#xff0c;减噪&#xff0c;去线&#xff0c;分割&#xff0c;二值化&#xff0c;提取特征码这里比较方便的是使用tesseract1&#xff0c;准备库apt-ge…

POJ_1253胜利的大逃亡

这道题使用BFS做的&#xff0c;刚开始有点不太理解为什么使用队列&#xff0c;一旦遇到可以到达终点的节点就立即返回&#xff0c;找到最短时间&#xff0c;最后明白了&#xff0c;因为在队列里的所有节点一定比队头节点 的时间长。下面是具体代码&#xff1a; #include<std…