關於組件

根組件

每個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.

相關文章
相關標籤/搜索