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,一经查实,立即删除!

相关文章

sourceTree添加git密钥步骤

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

background-size

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

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

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

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…

CVE-2014-4877 wget: FTP Symlink Arbitrary Filesystem Access

目录 1. 漏洞基本描述 2. 漏洞带来的影响 3. 漏洞攻击场景重现 4. 漏洞的利用场景 5. 漏洞原理分析 6. 漏洞修复方案 7. 攻防思考 1. 漏洞基本描述 0x1: Wget简介 wget是一个从网络上自动下载文件的自由工具&#xff0c;支持通过HTTP、HTTPS、FTP三个最常见的TCP/IP协议下载&am…

mysql-nt.exe w3wp.exe cpu 100%_w3wp.exe(IIS ) CPU 占用 100% 的常见原因及解决办法

对于IIS管理员来说&#xff0c;经常会碰到Web服务器CPU占用100%的情况&#xff0c;以下是个人的日常工作总结和一些解决办法&#xff0c;主要用来剖析w3wp.exe(IIS )占用CPU 100%的一些原因 和解决方案&#xff0c;希望能对你有所帮助w3wp.exe的解释:全名&#xff0c;IIS Appli…

TOP结果详解

2019独角兽企业重金招聘Python工程师标准>>> TOP前5行 top - 16:24:25 up 284 days, 4:59, 1 user, load average: 0.10, 0.05, 0.01 top 当前时间、系统启动时间、当前系统登录用户数目、平均负载&#xff08;1分钟,10分钟,15分钟&#xff09;。平均负载&#x…

BZOJ3236 [Ahoi2013]作业

昨天晚上做的。。。差错一直查到今天 最后没办法问管理员要了数据才知道原来ans数组开小了233&#xff0c;简直沙茶 这道题不就是裸的莫队嘛 ||| 只要用树状数组维护当前的两种个数即可。 1 /**************************************************************2 Problem: 3…

mysql ddl dml 导出_MySQL:DDL和DML语句,弄明白了吗?

语句分类DDL&#xff08;Data Definition Languages&#xff09;语句&#xff1a;即数据库定义语句&#xff0c;用来创建数据库中的表、索引、视图、存储过程、触发器等&#xff0c;常用的语句关键字有&#xff1a;CREATE,ALTER,DROP,TRUNCATE,COMMENT,RENAME。增删改表的结构D…

敏捷水手——单体法到微服务之旅

\本文要点\\探究持续四年多的渐进式改革是什么样子&#xff1b;\\t探索为什么在变革软件和组织设计时要遵循康威定律&#xff1b;\\t看看如何将领导力应用到不同的团队、领域和层级&#xff1b;\\t举例说明变革管理如何依赖于理念和一贯的长远目标&#xff1b;\\t了解从职能型团…

SQLCMD的介绍

SQLCMD的介绍 原文:SQLCMD的介绍文章转载自&#xff1a;http://blog.sina.com.cn/s/blog_3eec0ced0100mhm2.html最近经常用到超过80M *.sql文件的导入问题。上网找了一下&#xff0c;发现超过80M的文件是不能在查询分析器中执行的。找了些解决方案&#xff0c;个人感觉最简单的…

怎样下载C/C++的免费、开源且跨平台IDE——Code::Blocks

进入Code::Blocks的官网&#xff0c;官网地址为&#xff1a;http://www.codeblocks.org/home。进入后如下图所示&#xff1a; 点击“Home”菜单&#xff0c;跳转到IDE的下载界面&#xff1a; 有几种模式可供选择&#xff0c;我选择的第一种&#xff0c;Download the binary rel…

Logistic回归 python实现

Logistic回归 算法优缺点&#xff1a; 1.计算代价不高&#xff0c;易于理解和实现2.容易欠拟合&#xff0c;分类精度可能不高3.适用数据类型&#xff1a;数值型和标称型 算法思想&#xff1a; 其实就我的理解来说&#xff0c;logistic回归实际上就是加了个sigmoid函数的线性回归…

python 定时自动爬取_python实现scrapy爬虫每天定时抓取数据的示例代码

1. 前言。1.1. 需求背景。每天抓取的是同一份商品的数据&#xff0c;用来做趋势分析。要求每天都需要抓一份&#xff0c;也仅限抓取一份数据。但是整个爬取数据的过程在时间上并不确定&#xff0c;受本地网络&#xff0c;代理速度&#xff0c;抓取数据量有关&#xff0c;一般情…

博客园win8客户端开发记录5-app设置 登录 回复评论

这段时间完成了博客园cnblogs登录&#xff0c;注销和设置的相关功能 &#xff0c;进入软件&#xff0c; 打开win8的charm setting 选择设置就是当前软件的设置选项了&#xff0c; 感觉这有点山寨mac os x系统&#xff08;所有软件包括当前系统使用统一的设置&#xff09;。 扯远…