先吐槽下掘金的app,分享微信公眾號的文章,程式碼格式全亂了,索性重新編輯一遍。
React 深入系列,深入講解了React中的重點概念、特性和模式等,旨在幫助大家加深對React的理解,以及在專案中更加靈活地使用React。
React 中的元素、元件、例項和節點,是React中關係密切的4個概念,也是很容易讓React 初學者迷惑的4個概念。現在,老幹部就來詳細地介紹這4個概念,以及它們之間的聯絡和區別,滿足喜歡咬文嚼字、刨根問底的同學(老幹部就是其中一員)的好奇心。
元素 (Element)
React 元素其實就是一個簡單JavaScript物件,一個React 元素和介面上的一部分DOM對應,描述了這部分DOM的結構及渲染效果。一般我們通過JSX語法建立React 元素,例如:
const element = <h1 className='greeting'>Hello, world</h1>;
複製程式碼
element是一個React 元素。在編譯環節,JSX 語法會被編譯成對React.createElement()的呼叫,從這個函式名上也可以看出,JSX語法返回的是一個React 元素。上面的例子編譯後的結果為:
const element = React.createElement(
'h1',
{className: 'greeting'},
'Hello, world!'
);
複製程式碼
最終,element的值是類似下面的一個簡單JavaScript物件:
const element = {
type: 'h1',
props: {
className: 'greeting',
children: 'Hello, world'
}
}
複製程式碼
React 元素可以分為兩類:DOM型別的元素和元件型別的元素。DOM型別的元素使用像h1、div、p等DOM節點建立React 元素,前面的例子就是一個DOM型別的元素;元件型別的元素使用React 元件建立React 元素,例如:
const buttonElement = <Button color='red'>OK</Button>;
複製程式碼
buttonElement就是一個元件型別的元素,它的值是:
const buttonElement = {
type: 'Button',
props: {
color: 'red',
children: 'OK'
}
}
複製程式碼
對於DOM型別的元素,因為和頁面的DOM節點直接對應,所以React知道如何進行渲染。但是對於元件型別的元素,如buttonElement,React是無法直接知道應該把buttonElement渲染成哪種結構的頁面DOM,這時就需要元件自身提供React能夠識別的DOM節點資訊,具體實現方式在介紹元件時會詳細介紹。
有了React 元素,我們應該如何使用它呢?其實,絕大多數情況下,我們都不會直接使用React 元素,React 內部會自動根據React 元素,渲染出最終的頁面DOM。更確切地說,React元素描述的是React虛擬DOM的結構,React會根據虛擬DOM渲染出頁面的真實DOM。
元件 (Component)
React 元件,應該是大家最熟悉的React中的概念。React通過元件的思想,將介面拆分成一個個可以複用的模組,每一個模組就是一個React 元件。一個React 應用由若干元件組合而成,一個複雜元件也可以由若干簡單元件組合而成。
React元件和React元素關係密切,React元件最核心的作用是返回React元素。這裡你也許會有疑問:React元素不應該是由React.createElement() 返回的嗎?但React.createElement()的呼叫本身也是需要有“人”負責的,React元件正是這個“責任人”。React元件負責呼叫React.createElement(),返回React元素,供React內部將其渲染成最終的頁面DOM。
既然元件的核心作用是返回React元素,那麼最簡單的元件就是一個返回React元素的函式:
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
複製程式碼
Welcome是一個用函式定義的元件。如果使用類(class)定義元件,返回React元素的工作具體就由元件的render方法承擔,例如:
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
複製程式碼
其實,使用類定義的元件,render方法是唯一必需的方法,其他元件的生命週期方法都只不過是為render服務而已,都不是必需的。
現在來考慮下面這個例子:
class Home extends React.Component {
render() {
return (
<div>
<Welcome name='老幹部' />
<p>Anything you like</p>
</div>
)
}
}
複製程式碼
Home 元件使用了Welcome元件,返回的React元素為:
{
type: 'div',
props: {
children: [
{
type: 'Welcome',
props: {
name: '老幹部'
}
},
{
type: 'p',
props: {
children: 'Anything you like'
}
},
]
}
}
複製程式碼
對於這個結構,React 知道如何渲染type = 'div' 和 type = 'p' 的節點,但不知道如何渲染type='Welcome'的節點,當React 發現Welcome 是一個React 元件時(判斷依據是Welcome首字母為大寫),會根據Welcome元件返回的React 元素決定如何渲染Welcome節點。Welcome元件返回的React 元素為:
{
type: 'h1',
props: {
children: 'Hello, 老幹部'
}
}
複製程式碼
這個結構中只包含DOM節點,React是知道如何渲染的。如果這個結構中還包含其他元件節點,React 會重複上面的過程,繼續解析對應元件返回的React 元素,直到返回的React 元素中只包含DOM節點為止。這樣的遞迴過程,讓React 獲取到頁面的完整DOM結構資訊,渲染的工作自然就水到渠成了。
另外,如果仔細思考的話,可以發現,React 元件的複用,本質上是為了複用這個元件返回的React 元素,React 元素是React 應用的最基礎組成單位。
例項 (Instance)
這裡的例項特指React元件的例項。React 元件是一個函式或類,實際工作時,發揮作用的是React 元件的例項物件。只有元件例項化後,每一個元件例項才有了自己的props和state,才持有對它的DOM節點和子元件例項的引用。在傳統的物件導向的開發方式中,例項化的工作是由開發者自己手動完成的,但在React中,元件的例項化工作是由React自動完成的,元件例項也是直接由React管理的。換句話說,開發者完全不必關心元件例項的建立、更新和銷燬。
節點 (Node)
在使用PropTypes校驗元件屬性時,有這樣一種型別:
MyComponent.propTypes = {
optionalNode: PropTypes.node,
}
複製程式碼
PropTypes.node又是什麼型別呢?這表明optionalNode是一個React 節點。React 節點是指可以被React渲染的資料型別,包括數字、字串、React 元素,或者是一個包含這些型別資料的陣列。例如:
// 數字型別的節點
function MyComponent(props) {
return 1;
}
// 字串型別的節點
function MyComponent(props) {
return 'MyComponent';
}
// React元素型別的節點
function MyComponent(props) {
return <div>React Element</div>;
}
// 陣列型別的節點,陣列的元素只能是其他合法的React節點
function MyComponent(props) {
const element = <div>React Element</div>;
const arr = [1, 'MyComponent', element];
return arr;
}
// 錯誤,不是合法的React節點
function MyComponent(props) {
const obj = { a : 1}
return obj;
}
複製程式碼
最後總結一下,React 元素和元件的概念最重要,也最容易混淆;React 元件例項的概念大家瞭解即可,幾乎使用不到;React 節點有一定使用場景,但看過本文後應該也就不存在理解問題了。
下篇預告:
React 深入系列2:元件分類
我的新書《React進階之路》已上市,對React感興趣的同學不妨去了解下。 購買地址: 噹噹 京東
歡迎關注我的公眾號:老幹部的大前端