关于组件

根组件

每个React项目都会有一个根组件(固然是针对单页面的项目)。好比咱们常常使用的叫作 App的组件。html

ReactDOM负责将根组件挂载在指定的DOM元素上,通常咱们指定的是 id为app的div上。react

React中组件的转译之路。

React中咱们直接使用的jsx语法来写组件,可是,浏览器是不认识这些东西的,因此要想将组件真正的挂载在真实DOM上就须要对其进行转译。浏览器

①JSX组件

就是咱们原始的写法,采用jsx语法写成的组件,这些内容是定义在组件的render方法内。 好比:bash

import React from "react";
    import ReactDOM from "react-dom";
    class App extends React.Component {
      constructor(props) {
        super(props);
        this.handleClick = this.handleClick.bind(this);
        this.state = {
          count: 1
        };
      }
      componentWillMount() {
        this.setState({
          count: 13
        });
      }
      handleClick() {
        this.handleState();
      }
      handleState() {
        this.setState({
          count: this.state.count + 1
        });
      }
      render() {
        return <div onClick={this.handleClick}>this is app {this.state.count}</div>;
      }
    }
    
    export default App;
 
复制代码

咱们知道,要挂载在真实DOM上的只有render内的内容。而render内的内容明显不适合直接挂载,这就须要处理了。babel

②bable转译

咱们将上边的App组件使用babel转译一下,app

"use strict";

    var _interopRequireDefault = require("@babel/runtime-corejs2/helpers/interopRequireDefault");
    
    var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/classCallCheck"));
    
    var _createClass2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/createClass"));
    
    var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/possibleConstructorReturn"));
    
    var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/getPrototypeOf"));
    
    var _assertThisInitialized2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/assertThisInitialized"));
    
    var _inherits2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/inherits"));
    
    var _react = _interopRequireDefault(require("react"));
    
    var _reactDom = _interopRequireDefault(require("react-dom"));
    
    
    var App =
    /*#__PURE__*/
    function (_React$Component) {
      (0, _inherits2["default"])(App, _React$Component);
    
      function App(props) {
        var _this;
    
        (0, _classCallCheck2["default"])(this, App);
        _this = (0, _possibleConstructorReturn2["default"])(this, (0, _getPrototypeOf2["default"])(App).call(this, props));
        _this.handleClick = _this.handleClick.bind((0, _assertThisInitialized2["default"])(_this));
        _this.fileChange = _this.fileChange.bind((0, _assertThisInitialized2["default"])(_this));
        _this.state = {
          count: 1
        };
        return _this;
      }
    
      (0, _createClass2["default"])(App, [{
        key: "componentWillMount",
        value: function componentWillMount() {
          this.setState({
            count: 13
          });
        }
      }, {
        key: "handleClick",
        value: function handleClick() {
          this.handleState();
        }
      }, {
        key: "handleState",
        value: function handleState() {
          this.setState({
            count: this.state.count + 1
          });
        }
      }, {
        key: "render",
        value: function render() {
          return _react["default"].createElement("div", {
            onClick: this.handleClick
          }, "this is app ", this.state.count);
        }
      }]);
      return App;
    }(_react["default"].Component);
    
    _reactDom["default"].render(_react["default"].createElement(App, null), document.getElementById("app"));
复制代码

如上所示,这才是React组件在React中的真实样子。dom

③自定义组件的生成lement

注意:组件只有在Render方法执行的时候才会去构建Element。 这里ReactDOM也有一个render方法。以下:函数

ReactDOM.render(<App />, document.getElementById("app"));
复制代码

固然,它render是App组件自己也就是:ui

createElement(App, null)
复制代码

参数 App则是上边的App变量。 咱们先来看看App自己被建立为Element的样子:this

$$typeof: Symbol(react.element)
    type: ƒ App(props)
    key: null
    ref: null
    props: {}
    _owner: null
    _store: {validated: false}
    _self: null
    _source: null
    __proto__: Object
复制代码

如上所示,即是一个自定义组件,App组件被转换为Element的样子。

④类HTMl标签内容生成Element

接下来咱们摘取App组件本身的render方法来看看,以下。

{
        key: "render",
        value: function render() {
          return _react["default"].createElement("div", {
            onClick: this.handleClick
          }, "this is app ", this.state.count);
        }
    }
复制代码

这个render方法也是调用了createElement方法来建立一个Element。参数有三个:

第一个是:React规定每个组件的render函数返回的内容只容许有一个(HTML标签)。而这个就是第一个参数,用来当作真正的HTMl标签来建立HTMl元素。

第二个是:一个config,也就是组件内的jsx上写的属性,包括方法,样式等。

第三个极其以后的:不限个数的参数,表示组件内包含的内容。能够是文本能够是html标签,能够是自定义的另外的组件。

Babel转译以后,调用特定的方法来将组件生成为Element。先来看看Element是什么样子的

{
        $$typeof: Symbol(react.element)
        type: "div"
        key: null
        ref: null
        props: {children: Array(2), onClick: ƒ}
        
        _owner: ReactCompositeComponentWrapper {_currentElement: {…}, _rootNodeID: ".0", _instance: App, _pendingElement: null, _pendingStateQueue: null, …}
        
        _store: {validated: false}
        _self: null
        _source: null
        __proto__: Object
    }
    
复制代码

注意:自定义组件和类html标签生成的Element是能够互相嵌套的,到最后就会造成一个树形的结构,其形式就如同DOM树的另一种表示方法。每个自定义组件,每个自定义组件内的相似HTMl标签的内容都会有本身的Element。如此层层嵌套。

因此,简单的来讲,Element就是描述了一种DOM树结构。固然,只是描述,而和真正的DOM树差别很大,可是,彻底能够根据这个描述来建立一个真实的DOM树。

⑤挂载实例

组件被转换为Element并非结束,React会将Element再次进行转换为挂载实例

咱们先来看看App 组件 Element 的挂载实例。

_reactDom["default"].render()
复制代码

上边这个方法被调用的时候,是开始渲染的时刻。后边的文章会详细的讲述,这里咱们就简单的来讲一下。 在上边这个方法调用的时候最终会调用下边这行代码。 参数 nextElement 就是要实例的 Element。

componentInstance = instantiateReactComponent(nextElement, null);
复制代码

ReactDOM.render方法在挂载根组件的时候会调用上边的方法,这是一个递归的过程,将根组件包含的全部的内容都选择不一样的方式进行实例化。这一部分在接下来的文章 [Element实例化] (juejin.im/editor/draf…)的文章内又说明。

咱们这里就看一下挂载实例张什么样子。

⑥App的挂载实例

_currentElement:{
        $$typeof: Symbol(react.element)
        type: ƒ App(props)
        key: null
        ref: null
        props: {}
        _owner: null
        _store: {validated: false}
        _self: null
        _source: null
        __proto__: Object
    }
    _rootNodeID: ".0"
    _instance: App {props: {…}, context: {…}, refs: {…}, updater: {…}, handleClick: ƒ, …}
    _pendingElement: null
    _pendingStateQueue: null
    _pendingReplaceState: false
    _pendingForceUpdate: false
    _renderedComponent: ReactDOMComponent {_tag: "div", _renderedChildren: {…}, _previousStyle: null, _previousStyleCopy: null, _rootNodeID: ".0", …}
    _context: {__validateDOMNesting_ancestorInfo$30mp078jcnw: {…}}
    _mountOrder: 2
    _topLevelWrapper: ReactCompositeComponentWrapper {_currentElement: {…}, _rootNodeID: ".0", _instance: TopLevelWrapper, _pendingElement: null, _pendingStateQueue: null, …}
    _pendingCallbacks: null
    _mountIndex: 0
    _mountImage: null
    _isOwnerNecessary: false
    _warnedAboutRefsInRender: false
    __proto__: Object
复制代码

挂载实例分为不一样的类别参考文章。这里是自定义组件的挂载实例。咱们以前说过,React会递归的将组件内的全部的一切都实例化为挂载实例。即便是一段纯文本也会有本身的挂载实例。

⑥markup

每个挂载实例都会有本身的mountComponent方法,这个方法返回一个markup,全部的markup组合起来即是一个形式上的饿DOM树。 关于marku请参考文章.

最后

到此为止,React将咱们写的jsx代码转换为了能够直接挂载的markup。组件的转换之旅也就到此结束了。这一系列的过程,React作了不少的事情,好比,提取定义的事件注册一下等。

这是很重要的一条线:

jsx-->babel转译为 createElement方法的--->生成 Element---> 生成挂载实例--->挂载的时候生成 markup.

相关文章
相关标签/搜索