React的元件,元素和例項
React Components, Elements, and Instances
Elements Describe the Tree
在react
中,元素(element)就是描述元件例項或DOM節點及其所需屬性的普通物件。 它僅包含有關元件型別(例如,Button
元件),
其屬性(例如,顏色)以及其中的任何子元素的資訊。
並且元素也不是實際的元件例項。相反,它是一種告訴React你想在螢幕上看到什麼的方法。
你不能在元素上呼叫任何方法。它只是一個帶有兩個欄位的不可變描述物件:type: (string | ReactClass) && props: Object1
DOM Elements
當元素(element
)的型別是字串時,它表示具有該標記名稱的DOM
節點,並且props
對應其屬性。這就是React
將呈現的內容。例如:
{
type: 'button',
props: {
className: 'button button-blue',
children: {
type: 'b',
props: {
children: 'OK!'
}
}
}
}
此element
只是將以下HTML
表示為普通物件的一種方法:
<button class ='button button-blue'>
<b>
OK!
</b>
</button>
請注意元素如何巢狀。按照慣例,當我們想要建立一個元素樹時,我們將一個或多個子元素指定為其包含元素的children prop
。
重要的是,子元素和父元素都只是描述而不是實際的例項。
React
元素很容易遍歷,不需要解析,當然它們比實際的DOM
元素輕得多 - 它們只是物件!
Component Elements
我們知道,元素的型別(type)也可以是與React元件對應的函式或類:
{
type: Button,
props: {
color: 'blue',
children: 'OK!'
}
}
這是react
的核心理念
描述元件的元素也是一個元素,就像描述DOM節點的元素一樣。它們可以巢狀並相互混合。
此功能可以將DangerButton
元件定義為具有特定顏色屬性值的Button
,而無需擔心Button
是否呈現為DOM <button>
,<div>
或其他的標籤:
const DangerButton = ({ children }) => ({
type: Button,
props: {
color: 'red',
children: children
}
});
也可以在一個元素樹中匹配dom
或者component
的元素。如下:
const DeleteAccount = () => ({
type: 'div',
props: {
children: [{
type: 'p',
props: {
children: 'Are you sure?'
}
}, {
type: DangerButton,
props: {
children: 'Yep'
}
}, {
type: Button,
props: {
color: 'blue',
children: 'Cancel'
}
}]
});
或者可以用你jsx
的形式:
const DeleteAccount = () => (
<div>
<p>Are you sure?</p>
<DangerButton>Yep</DangerButton>
<Button color='blue'>Cancel</Button>
</div>
);
Components Encapsulate Element Trees
當React
看到一個具有函式(function)或類(class)型別的元素(element)時,它知道在給定相應的props的情況下向該元件詢問它呈現的元素。
比如他看到這麼一個元素:
{
type: Button,
props: {
color: 'blue',
children: 'OK!'
}
}
React
會詢問Button
渲染什麼。然後Button
會返回一個元素告訴他:
{
type: 'button',
props: {
className: 'button button-blue',
children: {
type: 'b',
props: {
children: 'OK!'
}
}
}
}
React
將重複此過程,直到它知道頁面上每個元件的底層DOM元素都被標記。
React
就像一個孩子,問你每個’X是Y’的’Y是什麼’,你向他們解釋,直到他們弄清楚世界上的每一件小事。
還記得上面的表單示例嗎?它可以用React編寫如下:
const Form = ({ isSubmitted, buttonText }) => {
if (isSubmitted) {
// Form submitted! Return a message element.
return {
type: Message,
props: {
text: 'Success!'
}
};
}
// Form is still visible! Return a button element.
return {
type: Button,
props: {
children: buttonText,
color: 'blue'
}
};
};
對於React
元件,props
是輸入,元素樹
是輸出。
返回的元素樹可以包含描述DOM節點的元素和描述其他元件的元素。這使得可以在不依賴於其內部DOM結構的情況下組成UI的獨立部分。
我們讓React
建立,更新和銷燬例項。並且使用從元件返回的元素來描述它們,React
負責管理例項。
Components Can Be Classes or Functions
在上面的例子中,Form
, Message
, Button
都是元件。我們可以把它用function
的方式展現,就像上面的那樣,也可以使用class
繼承自React.Component
。宣告元件的三種方式,可以像下面這樣:
// 1) As a function of props
const Button = ({ children, color }) => ({
type: 'button',
props: {
className: 'button button-' + color,
children: {
type: 'b',
props: {
children: children
}
}
}
});
// 2) Using the React.createClass() factory
const Button = React.createClass({
render() {
const { children, color } = this.props;
return {
type: 'button',
props: {
className: 'button button-' + color,
children: {
type: 'b',
props: {
children: children
}
}
}
};
}
});
// 3) As an ES6 class descending from React.Component
class Button extends React.Component {
render() {
const { children, color } = this.props;
return {
type: 'button',
props: {
className: 'button button-' + color,
children: {
type: 'b',
props: {
children: children
}
}
}
};
}
}
將元件定義為類時,它比功能元件更強大。它可以儲存一些本地狀態,並在建立或銷燬相應的DOM
節點時執行自定義方法邏輯。
函式元件功能較弱但更簡單,並且只使用一個render
方法。除非需要僅在class
中提供的功能,否則建議使用function
元件。
無論是函式還是類,從根本上說它們都是React
的元件。他們將props
作為輸入,並將元素作為輸出返回。
Top-Down Reconciliation
當我們呼叫下面程式碼時:
ReactDOM.render({
type: Form,
props: {
isSubmitted: false,
buttonText: 'OK!'
}
}, document.getElementById('root'));
react
會根據給定的props
詢問Form
元件返回什麼元素樹。
// React: 你告訴我這個
{
type: Form,
props: {
isSubmitted: false,
buttonText: 'OK!'
}
}
// React: Form告訴我這個
{
type: Button,
props: {
children: 'OK!',
color: 'blue'
}
}
// React: Button告訴我這個,此時預測已經結束。
{
type: 'button',
props: {
className: 'button button-blue',
children: {
type: 'b',
props: {
children: 'OK!'
}
}
}
}
這個是React
和解過程中的一部分,並且這個是在呼叫ReactDOM.render或setState時觸發的。
在和解結束後,React
會知道DOM
樹結果,並且像react-dom
或react-native
這樣的渲染器應用,以最小的更改集去更新DOM
節點(或者在React Native
的情況下,特定於平臺的檢視) 。
這種漸進的精煉過程也是React
應用程式易於優化的原因。如果元件樹的某些部分變得太大而React
無法有效訪問,就可以告訴它跳過這個“精煉”並在相關props
未更改的情況下區分樹的某些部分。如果它們是不可變的,那麼計算props
是否已經改變是非常快的,因此React
和immutability
可以很好地協同工作,並且可以用最小的努力提供很好的優化。
你可能已經注意到,此部落格條目對元件和元素進行了大量討論,而不是例項。事實上,例項在React
中的重要性遠遠低於大多數面向物件的UI
框架。
只有宣告為類的元件才有例項,而且你永遠不會直接建立它們:React
會為你做這件事。雖然存在父元件例項訪問子元件例項的機制,但它們僅用於命令性操作(例如將焦點設定在欄位上),並且通常應該避免。
React
負責為每個類元件建立一個例項,因此您可以使用方法和本地狀態以面向物件的方式編寫元件,但除此之外,例項在React
的程式設計模型中並不是非常重要,並且由React
本身管理。
總結
元素是一個普通物件,用於描述您希望在DOM
節點或其他元件方面在螢幕上顯示的內容。元素可以在其道具中包含其他元素。建立React
元素很簡單。一旦建立了一個元素,它就永遠不會發生改變。
元件
可以用幾種不同的宣告方式。它可以是一個帶有render
方法的類。或者,在簡單(你可以認為是沒有狀態)的情況下,可以將其定義為函式。在任何一種情況下,它都將props
作為輸入,並返回一個元素樹作為輸出。
當一個元件接收一些props
作為輸入時,這是因為一個特定的父元件返回了一個元素及其型別和這些props
。這就是人們說props
在React
中以一種方式流動的原因:從父母到孩子。
在編寫的元件類中,this
就是指的例項。它對於儲存本地狀態和對生命週期事件做出反應非常有用。
功能元件根本沒有例項。類元件有例項,但您永遠不需要直接建立元件例項–React
負責這一點。
最後,要建立元素,請使用React.createElement
,JSX
或元素工廠助手。不要在真實程式碼中將元素寫為普通物件 - 只要知道它們是引擎蓋下的普通物件。