zoukankan      html  css  js  c++  java
  • 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;
    View Code

    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                     'u4E3Bu9875:',
    33                     name
    34                 ),
    35                 _react2.default.createElement(
    36                     'div',
    37                     null,
    38                     _react2.default.createElement(
    39                         'p',
    40                         { onClick: this.handleClickEvent },
    41                         'u54C8u54C8u54C8u54C8'
    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);
    View Code

    通过看编译后的代码,我们得出以下关键词线索: 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 的解读,还没搞....

  • 相关阅读:
    关于生成并发唯一性流水号的解决方案
    父页面得到<iframe>
    struts2 convention配置中常见配置选项及说明
    Struts2下关于Calendar,date的一些处理
    怎样将用户名和密码保存到Cookie中?【转】
    如何调用用户控件(UserControl)的方法 .
    Struts遍历标签<s:iterator>总结 .
    在事业的开展上保持归零的心态
    这种日子最轻松,这样的人生最快乐
    诚实是人世间最珍贵的宝物,是每个人都应当坚守的伟大情操
  • 原文地址:https://www.cnblogs.com/JhoneLee/p/9482911.html
Copyright © 2011-2022 走看看