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.pswp.cn/news/259735.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/259735.shtml
英文地址,請注明出處:http://en.pswp.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…