每個React項目都會有一個根組件(固然是針對單頁面的項目)。好比咱們常常使用的叫作 App的組件。html
ReactDOM負責將根組件掛載在指定的DOM元素上,通常咱們指定的是 id爲app的div上。react
React中咱們直接使用的jsx語法來寫組件,可是,瀏覽器是不認識這些東西的,因此要想將組件真正的掛載在真實DOM上就須要對其進行轉譯。瀏覽器
就是咱們原始的寫法,採用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
咱們將上邊的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
注意:組件只有在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的樣子。
接下來咱們摘取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…)的文章內又說明。
咱們這裏就看一下掛載實例張什麼樣子。
_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會遞歸的將組件內的全部的一切都實例化爲掛載實例。即便是一段純文本也會有本身的掛載實例。
每個掛載實例都會有本身的mountComponent方法,這個方法返回一個markup,全部的markup組合起來即是一個形式上的餓DOM樹。 關於marku請參考文章.
到此爲止,React將咱們寫的jsx代碼轉換爲了能夠直接掛載的markup。組件的轉換之旅也就到此結束了。這一系列的過程,React作了不少的事情,好比,提取定義的事件註冊一下等。
這是很重要的一條線:
jsx-->babel轉譯爲 createElement方法的--->生成 Element---> 生成掛載實例--->掛載的時候生成 markup.