React 16 源码瞎几把解读 【二】 react组件的解析过程
一、一个真正的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 的解读,还没搞....