本文來自 React技術揭祕html
JSX
做爲描述組件內容的數據結構,爲JS賦予了更多視覺表現力。在React
中咱們大量使用他。在深刻源碼以前,有些疑問咱們須要先解決:react
JSX
和虛擬DOM是同一個東西麼?
React Component
、
React Element
是同一個東西麼,他們和
JSX
有什麼關係?
帶着這些疑問,讓咱們開始這一節的學習。git
相信做爲React
的使用者,你已經接觸過JSX
。若是你還不瞭解他,能夠看下官網對其的描述。github
JSX
在編譯時會被Babel
編譯爲React.createElement
方法。web
這也是爲何在每一個使用JSX
的JS文件中,你必須顯式的聲明數據結構
import React from 'react';
複製代碼
不然在運行時該模塊內就會報未定義變量 React
的錯誤。編輯器
你能夠經過@babel/plugin-transform-react-jsx插件告訴Babel
編譯時須要將JSX
編譯爲何函數的調用(默認爲React.createElement
)。函數
好比在preact這個類React
庫中,JSX
會被編譯爲一個名爲h
的函數調用。post
// 編譯前
<p>KaSong</p> // 編譯後 h("p", null, "KaSong"); 複製代碼
既然JSX
會被編譯爲React.createElement
,讓咱們看看他作了什麼:
export function createElement(type, config, children) {
let propName; const props = {}; let key = null; let ref = null; let self = null; let source = null; if (config != null) { // 將 config 處理後賦值給 props } const childrenLength = arguments.length - 2; // 處理 children,會被賦值給props.children // 處理 defaultProps return ReactElement( type, key, ref, self, source, ReactCurrentOwner.current, props, ); } const ReactElement = function(type, key, ref, self, source, owner, props) { const element = { // 標記這是個 React Element $typeof: REACT_ELEMENT_TYPE, type: type, key: key, ref: ref, props: props, _owner: owner, }; return element; }; 複製代碼
咱們能夠看到,React.createElement
最終會調用ReactElement
方法返回一個包含組件數據的對象,該對象有個參數$$typeof: REACT_ELEMENT_TYPE
標記了該對象是個React Element
。
因此調用React.createElement
返回的對象就是React Element
麼?
React
提供了驗證合法React Element
的全局API React.isValidElement,咱們看下他的實現:
export function isValidElement(object) {
return ( typeof object === 'object' && object !== null && object.$typeof === REACT_ELEMENT_TYPE ); } 複製代碼
能夠看到,$$typeof === REACT_ELEMENT_TYPE
的非null
對象就是一個合法的React Element
。換言之,在React
中,全部JSX
在運行時的返回結果(即React.createElement()
的返回值)都是React Element
。
那麼JSX
和React Component
的關係呢。
在React
中,咱們常使用ClassComponent
與FunctionComponent
構建組件。
class AppClass extends React.Component {
render() { return <p>KaSong</p> } } console.log('這是ClassComponent:', AppClass); console.log('這是Element:', <AppClass/>); function AppFunc() { return <p>KaSong</p>; } console.log('這是FunctionComponent:', AppFunc); console.log('這是Element:', <AppFunc/>); 複製代碼
咱們能夠從Demo控制檯打印的對象看出,ClassComponent
對應的Element
的type
字段爲AppClass
自身。
FunctionComponent
對應的Element
的type
字段爲AppFunc
自身,以下所示:
{
$typeof: Symbol(react.element), key: null, props: {}, ref: null, type: ƒ AppFunc(), _owner: null, _store: {validated: false}, _self: null, _source: null } 複製代碼
值得注意的一點,因爲
AppClass instanceof Function === true;
AppFunc instanceof Function === true; 複製代碼
因此沒法經過引用類型區分ClassComponent
和FunctionComponent
。React
經過ClassComponent
實例原型上的isReactComponent
變量判斷是不是ClassComponent
。
ClassComponent.prototype.isReactComponent = {};
複製代碼
從上面的內容咱們能夠發現,JSX
是一種描述當前組件內容的數據結構,他並不能描述組件schedule、reconcile、render相關的信息。好比以下信息就不包括在JSX
中:
state
這些內容都是包含在虛擬DOM中的。
因此,在組件mount
時,Reconciler
根據JSX
描述的組件內容生成組件對應的虛擬DOM。在update
時,Reconciler
將JSX
與虛擬DOM保存的數據對比,爲對比後狀態有變化的虛擬DOM打上標記。
經過這篇文章在運行時修改React.createElement
達到消除頁面全部div
元素的效果