有這樣的一個問題:javascript
// 方法定義 function add(x, y) { return x + y } // 方法調用 add(1, 2) // 組件定義 class Icon extends Component {} // 組件調用?????? <Icon />
最後的一句<Icon />
用專業的詞歸納是什麼操做,組件調用仍是什麼?html
有答「組件聲明」的,有答「組件調用的」,有「組件初始化」的,還有「使用一個組件」的。沒有一個統一的稱呼。形成這樣局面的緣由是不少時候咱們都沒有去詳細的瞭解過JSX和React實際操做之間的抽象層。如今咱們就深刻研究一下這部分知識。java
咱們來看看最基礎的問題,什麼是React?React就是一個用來寫界面的庫。無論React和React的生態有多複雜,最核心的功能就是用來寫界面的。那麼咱們來看看Element
,很簡單,可是一個React element描述的就是你想要在界面上看到的。再深刻一點,一個React element就是一個表明了DOM節點的對象。注意,一個React element並非在界面上實際繪製的東西,而是這些內容的表明。因爲JavaScript對象是輕量級的,React能夠任意的建立和銷燬這些element對象,並且不用擔憂太大的消耗。另外,React能夠分析這些對象,把當前的對象和以前的對象對比,找出發生的改變,而後根據實際發生的改變來更新實際的DOM。react
爲了建立一個DOM節點的表明對象(也就是React element),咱們可使用React的createElement
方法。spa
const element = React.createElement( 'div', {id: 'login-btn'}, 'Login' )
createElement
方法傳入了三個參數。第一個是標籤名稱字符串(div、span等),第二個是給element設置的屬性,第三個是內容或者是子的React element。本例中的「Login」就是element的內容。上面的createElement
方法調用後會返回一個這樣的對象:翻譯
{ type: 'div’, props: { children: 'Login', id: 'login-btn' } }
當這個對象繪製爲DOM(使用ReactDOM.render
方法)的時候,咱們就會有一個新的DOM節點:code
<div id='login-btn'>Login</div>
有一個頗有意思的地方,咱們在學React的時候首先注意到的就是component(組件)。「Components(組件)是React的構建塊」。注意,咱們是以element開始本文的。並且你一旦理解了element,理解component也就是水到渠成的事了。一個component就是一個方法或者一個類,能夠接受必定的輸入,以後返回一個React element。component
function Button({onLogin}) { return React.createElement( 'div', {id: 'login-btn', onClick: onLogin}, 'Login' ) }
在上面的定義中,咱們有一個Button
組件(component)。接收一個onLogin
輸入並返回一個React element。注意,Button
組件接收的onLogin
方法是它的prop
。而後把這個方法經過createElement
方法的第二個參數傳入到了實際的DOM裏。htm
目前,咱們只接觸到了使用HTML元素來建立React element,好比「div」、「span」等。其實,你也能夠把其餘的React component(組件)做爲第一個參數傳入createElement
方法。對象
const element = React.createElement( User, {name: 'Uncle Charlie'}, null )
然而,不一樣於通常的HTML標籤名稱,React若是發現第一個參數是class或者function類型的話,它就會檢查傳入的參數要繪製的是一個什麼element,傳入必要的props。以後React會一直檢查,直到沒有方法或者類做爲第一個參數傳入createElement
。咱們來看看下面的例子:
function Button({addFriend}) { return React.createElement( 'button', {onClick: addFriend}, 'Add Friend' ) } function User({name, addFriend}) { return React.createElement( 'div', null, React.createElement( 'p', null, name ), React.createElement(Button, {addFriend}) ) }
上面的例子裏有兩個component(組件)。一個Button,一個User。User「表明」了一個div,div裏面有兩個子節點:一個包含用戶名的「p」和一個Button組件。如今咱們看看上面的例子的具體的調用過程。
function Button({addFriend}) { return { type: 'button', props: { onClick: addFriend, children: 'Add Friend' } } } function User({name, addFreind}) { return { type: 'div', props: { children: [ { type: 'p', props: { children: name } }, { type: Button, props: { addFriend } } ] } } }
在上面的代碼裏你會看到四種不一樣的屬性:「button」,「div」,「p」和Button
。當React看到一個element是function和類類型的話,它就會檢查element會返回什麼element,並傳入對應的props。在這個過程結束以後,React就擁有了一個表明DOM樹的對象的數。上面的例子最後的結構是這樣的:
{ type: 'div', props: { children: [ { type: 'p', props: { children: 'Tyler McGinnis' } }, { type: 'button', props: { onClick: addFriend, children: 'Add Friend' } } ] } }
上面敘述的整個過程叫作Reconciliation(這個不知道怎麼翻譯,應該叫和諧?)。在React裏,每次調用setState
方法或ReactDOM.render
方法被調用的時候都會觸發這個過程。
那麼咱們來看看最開始的問題:
// 方法定義 function add(x, y) { return x + y } // 方法調用 add(1, 2) // 組件定義 class Icon extends Component {} // 組件調用?????? <Icon />
如今咱們已經有了回答這個問題的所有知識,除了一點點之外。有個地方,你可能以爲奇怪在使用React的時候,歷來沒有用過createElement
方法來建立element。你是用了JSX。我(做者)最開始的時候說:「主要緣由是歷來沒有去詳細的瞭解過JSX和React實際操做之間的抽象層」。這個抽象層就是JSX會被Babel轉碼爲React.createElement
方法的調用。
看看咱們前面的例子:
function Button({addFriend}) { return React.createElement( 'button', {onClick: addFriend}, 'Add Friend' ) } function User({ name, addFriend }) { return React.createElement( "div", null, React.createElement( "p", null, name ), React.createElement(Button, { addFriend }) ) }
寫成JSX的樣子是這樣的:
function Button({addFriend}) { return ( <button onClick={addFriend}>Add Friend</button> ) } function User({name, addFriend}) { return ( <div> <p>{name}</p> <Button addFriend={addFriend} /> </div> ) }
因此,最後咱們應該怎麼回答前面的問題呢?<Icon />
叫作什麼?
應該叫作「建立element」,應爲JSX最後會轉碼爲createElement
方法的調用:
React.createElement(Icon, null)
前面的例子都是「建立一個React element」。
React.createElement( 'div', { className: 'container' }, 'Hello!' ) <div className='container'>Hello!</div> <Hello />