一、一個真正的react組件編譯后長啥樣?
我們瞎幾把解讀了react 虛擬dom對象是怎么生成的,生成了一個什么樣的解構。一個react組件不光由若干個這些嵌套的虛擬dom對象組成,還包括各種生命周期鈎子、自定義方法、事件等組成
下面讓我們繼續探索
react組件寫法:

1 // 一個再普通不過的react組件寫法 2 3 4 mport React,{Component} from 'react'; 5 import Header from '../components/header'; 6 class Home extends Component { 7 constructor(props){ 8 super(props); 9 } 10 componentWillMount(){ 11 console.log('willMount'); 12 } 13 handleClickEvent(){ 14 console.log('click'); 15 } 16 render(){ 17 let {name} = this.props; 18 return ( 19 <div ref="home"> 20 <Header kk="js"/> 21 <div>主頁:{name}</div> 22 <div> 23 <p onClick={this.handleClickEvent}>哈哈哈哈</p> 24 </div> 25 </div> 26 ) 27 } 28 } 29 30 export default Home;
react組件被babel-preset-react編譯后

1 var Home = function (_Component) { 2 _inherits(Home, _Component); 3 4 function Home(props) { 5 _classCallCheck(this, Home); 6 7 return _possibleConstructorReturn(this, (Home.__proto__ || Object.getPrototypeOf(Home)).call(this, props)); 8 } 9 10 _createClass(Home, [{ 11 key: 'componentWillMount', 12 value: function componentWillMount() { 13 console.log('willMount'); 14 } 15 }, { 16 key: 'handleClickEvent', 17 value: function handleClickEvent() { 18 console.log('click'); 19 } 20 }, { 21 key: 'render', 22 value: function render() { 23 var name = this.props.name; 24 25 return _react2.default.createElement( 26 'div', 27 { ref: 'home' }, 28 _react2.default.createElement(_header2.default, { kk: 'js' }), 29 _react2.default.createElement( 30 'div', 31 null, 32 '\u4E3B\u9875:', 33 name 34 ), 35 _react2.default.createElement( 36 'div', 37 null, 38 _react2.default.createElement( 39 'p', 40 { onClick: this.handleClickEvent }, 41 '\u54C8\u54C8\u54C8\u54C8' 42 ) 43 ) 44 ); 45 } 46 }, { 47 key: '__reactstandin__regenerateByEval', 48 // @ts-ignore 49 value: function __reactstandin__regenerateByEval(key, code) { 50 // @ts-ignore 51 this[key] = eval(code); 52 } 53 }]); 54 55 return Home; 56 }(_react.Component);
通過看編譯后的代碼,我們得出以下關鍵詞線索: React.Component
二、React.Component 又干了什么
Component來自於 ReactBaseClasses.js 找到他!
import {Component, PureComponent} from './ReactBaseClasses';
function Component(props, context, updater) { this.props = props; // 眼熟的props this.context = context; // context this.refs = emptyObject; // 初始化refs this.updater = updater || ReactNoopUpdateQueue; } Component.prototype.isReactComponent = {}; // 經典的setState 方法 Component.prototype.setState = function(partialState, callback) { ... }; // 強制重繪 Component.prototype.forceUpdate = function(callback) { this.updater.enqueueForceUpdate(this, callback, 'forceUpdate'); };
通過閱讀代碼,我們發現Component這個類的構成其實並不復雜,但其中的updater是一個很重要的東西,不過今天略過不表,脫離生命周期及dom渲染看updater沒有任何意義,以后再說
同樣setState也以后再說
通過js中的extends, 本文中的home組件獲得了Component類中的所有屬性和方法,我們再看源碼,看看babel是如何拆解react組件生命周期的
三、defineProperty 的一頓猛操作
babel在解析jsx的時候自己定義了一堆模擬es6 extends、 class 、super的一堆東西
通過查看解析后的源碼,我們可以知道其中奧秘
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
一個仿造的繼承,一個仿造的createClass
前者通過_inherits(Home,Component) 將 Component的prototype 賦予到Home上,使Home擁有了setState方法等等
后者通過 Object.defineProperty 將key-value形式的對象 賦予到 Home.prototype上
回首看被babel表一出來的react組件,那些鈎子函數、自定義方法都被搞成了一個個key-value形式的對象,通過_createClass 給綁定到了Home類中
這樣一個組件類就做好了,這個組件類的prototype里面有自定義函數、生命周期鈎子函數、render方法
然后就是通過createElement又再次的封裝成react 虛擬dom 被放到ReactDOM 中等待render
// 編譯前 ReactDOM.render( <div> <Home name="home"/> </div> , document.getElementById('app') ); // 編譯后 _reactDom2.default.render(_react2.default.createElement( 'div', null, _react2.default.createElement(_home2.default, { name: 'home' }) ), document.getElementById('app'));
只不過這種虛擬dom和其他的不太一樣,這種的對象里面的type類型是函數,而不是字符串罷了。所以可見 createElement返回對象的type不一定是字符串,是一切皆有可能
要知render中發生了什么,請聽下回分解
四、本期留坑
setState 的解讀,還沒搞....