react 调用组件方法_React源码分析1 — 组件和对象的创建(createClass,createElement)...
1 組件的創(chuàng)建
學習了半年前端了,感覺前端的水確實也很深。做安卓的時候就對React-Native比較感興趣,開發(fā)H5時也使用了一段時間的ReactJS。所以決定好好分析下它的源碼。文章中有不對的地方,請大神不吝賜教。
React受大家歡迎的一個重要原因就是可以自定義組件。這樣的一方面可以復用開發(fā)好的組件,實現(xiàn)一處開發(fā),處處調用,另外也能使用別人開發(fā)好的組件,提高封裝性。另一方面使得代碼結構很清晰,組件間耦合減少,方便維護。ES5創(chuàng)建組件時,調用React.createClass()即可. ES6中使用class myComponent extends React.Component, 其實內(nèi)部還是調用createClass創(chuàng)建組件。
組件創(chuàng)建我們可以簡單類比為Java中ClassLoader加載class。下面來分析下createClass的源碼,我們省去了開發(fā)階段錯誤提示的相關代碼,如propType的檢查。(if ("development" !== 'production') {}代碼段都不進行分析了,這些只在開發(fā)調試階段調用)
createClass: function (spec) {var Constructor = function (props, context, updater) {// 觸發(fā)自動綁定if (this.__reactAutoBindPairs.length) {bindAutoBindMethods(this);}// 初始化參數(shù)this.props = props;this.context = context;this.refs = emptyObject; // 本組件對象的引用,可以利用它來調用組件的方法this.updater = updater || ReactNoopUpdateQueue;// 調用getInitialState()來初始化state變量this.state = null;var initialState = this.getInitialState ? this.getInitialState() : null;this.state = initialState;};// 繼承父類Constructor.prototype = new ReactClassComponent();Constructor.prototype.constructor = Constructor;Constructor.prototype.__reactAutoBindPairs = [];injectedMixins.forEach(mixSpecIntoComponent.bind(null, Constructor));mixSpecIntoComponent(Constructor, spec);// 調用getDefaultProps,并掛載到組件類上。defaultProps是類變量,使用ES6寫法時更清晰if (Constructor.getDefaultProps) {Constructor.defaultProps = Constructor.getDefaultProps();}// React中暴露給應用調用的方法,如render componentWillMount。// 如果應用未設置,則將他們設為nullfor (var methodName in ReactClassInterface) {if (!Constructor.prototype[methodName]) {Constructor.prototype[methodName] = null;}}return Constructor;},createClass主要做的事情有
2 對象的創(chuàng)建
JSX中創(chuàng)建React元素最終會被babel轉譯為createElement(type, config, children), babel根據(jù)JSX中標簽的首字母來判斷是原生DOM組件,還是自定義React組件。如果首字母大寫,則為React組件。這也是為什么ES6中React組件類名必須大寫的原因。如下面代碼
<div className="title" ref="example"><span>123</span> // 原生DOM組件,首字母小寫<ErrorPage title='123456' desc={[]}/> // 自定義組件,首字母大寫 </div>轉譯完后是
React.createElement('div', // type,標簽名,原生DOM對象為String{className: 'title',ref: 'example'}, // config,屬性React.createElement('span', null, '123'), // children,子元素React.createElement(// type,標簽名,React自定義組件的type不為String.// _errorPage2.default為從其他文件中引入的React組件_errorPage2.default, {title: '123456',desc: []}) // children,子元素 )下面來分析下createElement的源碼
ReactElement.createElement = function (type, config, children) {var propName;// 初始化參數(shù)var props = {};var key = null;var ref = null;var self = null;var source = null;// 從config中提取出內(nèi)容,如ref key propsif (config != null) {ref = config.ref === undefined ? null : config.ref;key = config.key === undefined ? null : '' + config.key;self = config.__self === undefined ? null : config.__self;source = config.__source === undefined ? null : config.__source;// 提取出config中的prop,放入props變量中for (propName in config) {if (config.hasOwnProperty(propName) && !RESERVED_PROPS.hasOwnProperty(propName)) {props[propName] = config[propName];}}}// 處理children,掛到props的children屬性下// 入?yún)⒌那皟蓚€為type和config,后面的就都是children參數(shù)了。故需要減2var childrenLength = arguments.length - 2;if (childrenLength === 1) {// 只有一個參數(shù)時,直接掛到children屬性下,不是array的方式props.children = children;} else if (childrenLength > 1) {// 不止一個時,放到array中,然后將array掛到children屬性下var childArray = Array(childrenLength);for (var i = 0; i < childrenLength; i++) {childArray[i] = arguments[i + 2];}props.children = childArray;}// 取出組件類中的靜態(tài)變量defaultProps,并給未在JSX中設置值的屬性設置默認值if (type && type.defaultProps) {var defaultProps = type.defaultProps;for (propName in defaultProps) {if (props[propName] === undefined) {props[propName] = defaultProps[propName];}}}// 返回一個ReactElement對象return ReactElement(type, key, ref, self, source, ReactCurrentOwner.current, props); };下面來看ReactElement源碼
var ReactElement = function (type, key, ref, self, source, owner, props) {var element = {// This tag allow us to uniquely identify this as a React Element$$typeof: REACT_ELEMENT_TYPE,// ReactElement對象上的四個變量,特別關鍵type: type,key: key,ref: ref,props: props,// Record the component responsible for creating this element._owner: owner};return element; }可以看到僅僅是給ReactElement對象內(nèi)的成員變量賦值而已,不在贅述。
流程如如下
3 組件對象初始化
在mountComponent()掛載組件中,會進行組件渲染,調用到instantiateReactComponent()方法。這個過程我們在React生命周期方法中再詳細講述,這里有個大體了解即可。instantiateReactComponent()根據(jù)ReactElement中不同的type字段,創(chuàng)建不同類型的組件對象。源碼如下
// 初始化組件對象,node是一個ReactElement對象,是節(jié)點元素在React中的表示 function instantiateReactComponent(node) {var instance;var isEmpty = node === null || node === false;if (isEmpty) {// 空對象instance = ReactEmptyComponent.create(instantiateReactComponent);} else if (typeof node === 'object') {// 組件對象,包括DOM原生的和React自定義組件var element = node;// 根據(jù)ReactElement中的type字段區(qū)分if (typeof element.type === 'string') {// type為string則表示DOM原生對象,比如div span等。可以參看上面babel轉譯的那段代碼instance = ReactNativeComponent.createInternalComponent(element);} else if (isInternalComponentType(element.type)) {// 保留給以后版本使用,此處暫時不會涉及到instance = new element.type(element);} else {// React自定義組件instance = new ReactCompositeComponentWrapper(element);}} else if (typeof node === 'string' || typeof node === 'number') {// 元素是一個string時,對應的比如<span>123</span> 中的123// 本質上它不是一個ReactElement,但為了統(tǒng)一,也按照同樣流程處理,稱為ReactDOMTextComponentinstance = ReactNativeComponent.createInstanceForText(node);} else {// 無處理}// 初始化參數(shù),這兩個參數(shù)是DOM diff時用到的instance._mountIndex = 0;instance._mountImage = null;return instance; }故我們可以看到有四種創(chuàng)建組件元素的方式,同時對應四種ReactElement
下面分別分析這幾種對象,和創(chuàng)建他們的過程。
ReactDOMEmptyComponent
由ReactEmptyComponent.create()創(chuàng)建,最終生成ReactDOMEmptyComponent對象,源碼如下
var emptyComponentFactory;var ReactEmptyComponentInjection = {injectEmptyComponentFactory: function (factory) {emptyComponentFactory = factory;} };var ReactEmptyComponent = {create: function (instantiate) {return emptyComponentFactory(instantiate);} };ReactEmptyComponent.injection = ReactEmptyComponentInjection;ReactInjection.EmptyComponent.injectEmptyComponentFactory(function (instantiate) {// 前面比較繞,關鍵就是這句話,創(chuàng)建ReactDOMEmptyComponent對象return new ReactDOMEmptyComponent(instantiate); });// 各種null,就不分析了 var ReactDOMEmptyComponent = function (instantiate) {this._currentElement = null;this._nativeNode = null;this._nativeParent = null;this._nativeContainerInfo = null;this._domID = null; };ReactDOMComponent
由ReactNativeComponent.createInternalComponent()創(chuàng)建。這里注意原生組件不代表是DOM組件,而是React封裝過的Virtual DOM對象。React并不直接操作原生DOM。
大家可以自己看ReactDOMComponent的源碼。重點看下ReactDOMComponent.Mixin
ReactDOMComponent.Mixin = {mountComponent: function (transaction, nativeParent, nativeContainerInfo, context) {},_createOpenTagMarkupAndPutListeners: function (transaction, props){},_createContentMarkup: function (transaction, props, context) {},_createInitialChildren: function (transaction, props, context, lazyTree) {}receiveComponent: function (nextElement, transaction, context) {},updateComponent: function (transaction, prevElement, nextElement, context) {},_updateDOMProperties: function (lastProps, nextProps, transaction) {},_updateDOMChildren: function (lastProps, nextProps, transaction, context) {},getNativeNode: function () {},unmountComponent: function (safely) {},getPublicInstance: function () {} }其中暴露給外部的比較關鍵的是mountComponent,receiveComponen, updateComponent,unmountComponent。他們會引發(fā)React生命周期方法的調用,下一節(jié)再講。
ReactCompositeComponent
由new ReactCompositeComponentWrapper()創(chuàng)建,重點看下ReactCompositeComponentMixin
var ReactCompositeComponentMixin = {// new對應的方法,創(chuàng)建ReactCompositeComponent對象construct: function(element) {},mountComponent, // 初始掛載組件時被調用,僅一次performInitialMountWithErrorHandling, // 和performInitialMount相近,只是多了錯誤處理performInitialMount, // 執(zhí)行mountComponent的渲染階段,會調用到instantiateReactComponent,從而進入初始化React組件的入口getNativeNode,unmountComponent, // 卸載組件,內(nèi)存釋放等工作receiveComponent,performUpdateIfNecessary,updateComponent, // setState后被調用,重新渲染組件attachRef, // 將ref指向組件對象,這樣我們就可以利用它調用對象內(nèi)的方法了detachRef, // 將組件的引用從全局對象refs中刪掉,這樣我們就不能利用ref找到組件對象了instantiateReactComponent, // 初始化React組件的入口,在mountComponent時的渲染階段會被調用 }ReactDOMTextComponent
由ReactNativeComponent.createInstanceForText()創(chuàng)建,我們也不細細分析了,主要入口代碼如下,大家可以自行分析。
var ReactDOMTextComponent = function (text) {this._currentElement = text;this._stringText = '' + text; };_assign(ReactDOMTextComponent.prototype, {mountComponent,receiveComponent,getNativeNode,unmountComponent }總結
以上是生活随笔為你收集整理的react 调用组件方法_React源码分析1 — 组件和对象的创建(createClass,createElement)...的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ggplot2设置坐标轴范围_Matpl
- 下一篇: pytorch自带网络_【方家之言】一篇