在初學 React 的時候,分不清 React 組件和 React 元素,着實踩了一些坑。搞清楚 React 中什麼是組件,什麼是元素,既能夠理清楚概念,也可讓你避免一些沒必要要的錯誤。數組
React 元素(React element),它是 React 中最小基本單位,咱們可使用 JSX 語法輕鬆地建立一個 React 元素:數據結構
const element = <div className="element">I'm element</div>
React 元素不是真實的 DOM 元素,它僅僅是 js 的普通對象(plain objects),因此也沒辦法直接調用 DOM 原生的 API。上面的 JSX 轉譯後的對象大概是這樣的:函數
{ _context: Object, _owner: null, key: null, props: { className: 'element', children: 'I'm element' }, ref: null, type: "div" }
只有在這個元素渲染被完成後,才能經過選擇器的方式獲取它對應的 DOM 元素。不過,按照 React 有限狀態機的設計思想,應該使用狀態和屬性來表述組件,要儘可能避免 DOM 操做,即使要進行 DOM 操做,也應該使用 React 提供的接口ref
和getDOMNode()
。通常使用 React 提供的接口就足以應付須要 DOM 操做的場景了,所以像 jQuery 強大的選擇器在 React 中幾乎沒有用武之地了。 this
除了使用 JSX 語法,咱們還可使用 React.createElement()
和 React.cloneElement()
來構建 React 元素。spa
JSX 語法就是用React.createElement()
來構建 React 元素的。它接受三個參數,第一個參數能夠是一個標籤名。如div
、span
,或者 React 組件。第二個參數爲傳入的屬性。第三個以及以後的參數,皆做爲組件的子組件。設計
React.createElement( type, [props], [...children] )
React.cloneElement()
與React.createElement()
類似,不一樣的是它傳入的第一個參數是一個 React 元素,而不是標籤名或組件。新添加的屬性會併入原有的屬性,傳入到返回的新元素中,而就的子元素獎盃替換。code
React.cloneElement( element, [props], [...children] )
React 中有三種構建組件的方式。React.createClass()
、ES6 class
和無狀態函數。對象
React.createClass()
是三種方式中最先,兼容性最好的方法。在0.14版本前官方指定的組件寫法。blog
var Greeting = React.createClass({ render: function() { return <h1>Hello, {this.props.name}</h1>; } });
ES6 class
是目前官方推薦的使用方式,它使用了ES6標準語法來構建,但它的實現還是調用React.createClass()
來實現了,ES6 class
的生命週期和自動綁定方式與React.createClass()
略有不一樣。接口
class Greeting extemds React.Component{ render: function() { return <h1>Hello, {this.props.name}</h1>; } };
無狀態函數是使用函數構建的無狀態組件,無狀態組件傳入props
和context
兩個參數,它沒有state
,除了render()
,沒有其它生命週期方法。
function Greeting (props) { return <h1>Hello, {props.name}</h1>; }
React.createClass()
和ES6 class
構建的組件的數據結構是類,無狀態組件數據結構是函數,它們在 React 被視爲是同樣的。
組件是由元素構成的。元素數據結構是普通對象,而組件數據結構是類或純函數。除此以外,還有幾點區別要注意:
在 JSX 中,被元素嵌套的元素會以屬性 children 的方式傳入該元素的組件。當僅嵌套一個元素時,children 是一個 React 元素,當嵌套多個元素時,children 是一個 React 元素的數組。能夠直接把 children 寫入 JSX 的中,但若是要給它們傳入新屬性,就要用到React.cloneElement()
來構建新的元素。我曾放過如下錯誤:
render () { var Child = this.props.children return <div><Child tip={'error!'}/><div> }
由於 Child 是一個 React 元素,而不是組件,這樣的寫法是徹底錯誤的,正確的方式應該是:
render () { var child = this.props.children return <div>{ React.cloneElement(child, {tip: 'right way!'}) }<div> }
就這樣,原有屬性和新添加的屬性被一併傳入了子元素。使用React.cloneElement()
纔是操做元素的正確姿式。
有的時候,組件可讓用戶以屬性的方式傳入自定義的組件,來提高組件的靈活性。這個屬性傳入的就應該是 React 元素,而非 React 組件。使用 React 元素可讓用戶傳入自定義組件的同時,爲組件添加屬性。一樣,可使用React.cloneElement()
爲自定義組件添加更多屬性,或替換子元素。
// 推薦 <MyComponent tick={ <UserComponent tip="Yes"/> } /> // 不推薦 <MyComponent tick={ UserComponent } />
最後,打個不恰當的比喻,React 組件是MyComponent
,React 元素就是<MyComponent />
。