系列文章目錄
第一章:React從入門到進階之初識React
第一章:React從入門到進階之JSX簡介
第三章:React從入門到進階之元素渲染
第四章:React從入門到進階之JSX虛拟DOM渲染為真實DOM的原理和步驟
第五章:React從入門到進階之元件化開發及Props屬性傳值
第六章:React從入門到進階之state及元件的生命周期
第七章:React從入門到進階之React事件處理
文章目錄
-
-
- 系列文章目錄
- 一、React元件的定義及分類
- 二、函數式元件
- 三、類元件
- 四、元件的渲染
- 五、組合元件
- 六、提取元件
- 七、Props
-
一、React元件的定義及分類
- 元件允許我們可以将UI拆分成一個個獨立的可複用的代碼片段,并且可以對每個片段進行獨立構思和管理。
- 從概念上講,元件類似于JavaScript中的函數,它接收任意的輸入參數(props),并傳回用于描述頁面展示内容的React元素
- React 元件可以定義為類(class)元件或函數(function)元件的形式
二、函數式元件
下面我們以一個時鐘元件為例編寫一個函數式元件
- 函數式元件其實就是寫一個JavaScript中的函數,接收一個props對象作為參數,并傳回用于描述頁面内容的React元素(JSX文法),這就構成了一個函數式元件。
- 需要注意的是:元件名稱必須以大寫字母開頭,因為React 會将以小寫字母開頭的元件視為原生 DOM 标簽。例如,< div /> 代表 HTML 的 div 标簽,而 < Clock/> 則代表一個元件,并且需在作用域内使用 Clock。
- 函數式元件屬于靜态元件,調取元件渲染出的結果,除非重新渲染元件,否則第一次渲染的内容不會改變(但可以通過React HOOKS解決)
上面代碼就是一個有效的React元件,因為它接收唯一帶有資料的 “props”(代表屬性)對象與并傳回一個 React 元素(JSX文法),這類元件就被稱為“函數元件”,因為它本質上就是一個JavaScript函數。function Clock(props){ return <div>{new Date().toLocaleString()}</div> }
三、類元件
下面我們還是以時鐘元件為例:編寫一個類元件
- 上面我們已經說過,定義一個React元件可以有兩種方式,除了定義函數元件外,我們還可以使用ES6中的類(class)來定義React元件
- 類元件顧名思義:就是通過聲明一個類來實作定義一個React元件;定義類元件有如下幾個特點:
- 1.定義類元件必須要繼承自React的Component類或React.PureComponent
- 2.類中必須要定義一個名為render的函數,函數的傳回值應該是React元素(JSX文法)
- 3.在類的内部預設會有個props屬性(繼承自Component)可以直接使用
- 4.與函數元件一樣,元件名稱必須以大寫字母開頭
- 5.在類的構造函數(constructor)中通過this.props通路屬性值是擷取不到的,因為這時props還沒有挂載到this.props上,要等constructor執行完成之後才會挂載
- 6.如果非要在構造函數(constructor)中使用this.props來擷取,則可以把外面傳進來的props傳遞給constructor中的父類構造函數super進而實作this.props的挂載
- 7.類元件是動态元件,基于資料驅動視圖渲染
class Clock extends React.Component{ render(){ return <div>{new Date().toLocaleString()}</div> } }
四、元件的渲染
- 前面我們接觸到的React元素都隻是DOM标簽,比如div、h1、span等等。那麼我們自己定義的元件該如何渲染呢?
- 其實不管是函數元件還是類元件,渲染方式跟React元素是一樣的,也是把函數名或類名當做普通标簽直接使用即可
- 元件跟普通DOM标簽一樣,既可以是單閉合也可以是雙閉合,如果标簽内需要顯示文本或者是其它子元素,這時就需要用雙閉合标簽包起來
- 在使用過程中,我們也可以給元件傳遞各種屬性,這些屬性(attributes)以及元件内部的子元件(children)會被轉換為單個對象傳遞個元件,這個對象被稱為“props”,就是在函數元件或類元件定義時我們提到的那個props
- ReactDOM在将元件轉換為虛拟DOM時(轉換為React.createElement(xxx)),會進行判斷,如果生成的虛拟DOM對象的type是一個函數或者類(也就是說渲染的是一個函數元件或類元件),則首先會把函數執行并把解析出來的props傳遞給函數,如果是一個類,則會建立類的執行個體并把解析出來的props傳遞給這個類
- 然後在函數元件或類元件的内部我們就可以直接通過props.xxx或this.props.xxx來擷取和使用元件渲染時傳遞過來的屬性
- 傳遞進來的屬性是隻讀的(隻能擷取不能修改裡面的值),如果想要修改可以通過一個中間變量(狀态state)來進行操作
上述代碼運作結果,會在頁面上顯示 “Hello, Alvin”,Alvin就是我們通過name屬性動态傳遞給Welcome元件的function Welcome(props){ return <h1>Hello, {props.name}</h1> } ReactDOM.render( <Welcome name="Alvin" />,//單閉合标簽 document.getElementById("root") );
- 上面的例子中,我們通過調用ReactDOM的render函數,并把< Welcome name=“Alvin” />作為參數傳入
- React調用Welcome元件,并将{name:“Alvin”}作為參數傳遞給props,這個過程中元件Welcome會被作為普通函數執行
- 然後在Welcome元件中,将 < h1>Hello, Alvin</ h1>元素作為傳回值傳回
- 最後通過ReactDOM 将生成的虛拟DOM更新到真實的DOM中,将真實DOM更新為:< h1>Hello, Alvin</ h1>
五、組合元件
在日常項目開發中,我們自定義的元件中也可直接引入其它自定義元件作為子元素,這樣我們就可以用同一元件來抽象出任意層次的細節,比如按鈕、表單、對話框乃至整個螢幕等等,都可以以單獨元件的形式來表示,然後再通過其它元件來使用。
例如,我們可以建立一個包含多個元件的App元件,在這個App元件中可以包含多次渲染的同一個Welcome元件,也可以是另外的Clock元件,還可以是原生的DOM元素标簽。
function Clock(props){ return <div>{new Date().toLocaleString()}</div> } function Welcome(props){ return <h1 style={{color:'red'}}>Hello, {props.name}</h1>; } function App(props){ return <div> <Clock /> <Welcom name="Alvin" /> <Welcom name="Yinnes" /> <Welcom name="Yaolu" /> </div> } ReactDOM.render( <App />, document.getElementById('root') );
六、提取元件
接下來我們以一個Comment元件為例進行提取
- 上面一節中我們将了組合元件,就是在一個元件中引入多個相同或不同的其它元件。
- 下面我們要介紹的是提取并封裝元件,雖然說是拆分元件但其實跟上面的組合元件也類似,拆分後還是要進行組合使用。
- 有時候我們在寫一些複雜的業務邏輯時,可能就需要些很多代碼,那麼過多的邏輯代碼堆在一起,就很容易引起一些問題,并且後期也不利于維護,這時我們就需要把一些獨立的業務邏輯拆分出來封裝成一個獨立的元件,然後再通過組合元件進行使用。
function Comment(props) { return ( <div className="Comment"> <div className="UserInfo"> <img className="Avatar" src={props.author.avatarUrl} alt={props.author.name} /> <div className="UserInfo-name"> {props.author.name} </div> </div> <div className="Comment-text"> {props.text} </div> <div className="Comment-date"> {formatDate(props.date)} </div> </div> ); }
首先,我們将提取 Avatar 元件:
- 該元件用于描述一個社交媒體網站上的評論功能,它接收 author(對象),text (字元串)以及 date(日期)作為 props。
- 該元件由于嵌套的關系,變得難以維護,且很難複用它的各個部分。是以,讓我們從中提取一些元件出來。
function Avatar(props) { return ( <img className="Avatar" src={props.user.avatarUrl} alt={props.user.name} /> ); }
- Avatar 不需知道它在 Comment 元件内部是如何渲染的。是以,我們給它的 props 起了一個更通用的名字:user,而不是 author。
我們建議從元件自身的角度命名 props,而不是依賴于調用元件的上下文命名。
我們現在針對 Comment 做些微小調整:
接下來,我們将提取 UserInfo 元件,該元件在使用者名旁渲染 Avatar 元件:function Comment(props) { return ( <div className="Comment"> <div className="UserInfo"> <Avatar user={props.author} /> <div className="UserInfo-name"> {props.author.name} </div> </div> <div className="Comment-text"> {props.text} </div> <div className="Comment-date"> {formatDate(props.date)} </div> </div> ); }
進一步簡化 Comment 元件function UserInfo(props) { return ( <div className="UserInfo"> <Avatar user={props.user} /> <div className="UserInfo-name"> {props.user.name} </div> </div> ); }
最初看上去,提取元件可能是一件繁重的工作,但是,在大型應用中,建構可複用元件庫是完全值得的。根據經驗來看,如果 UI 中有一部分被多次使用(Button,Panel,Avatar),或者元件本身就足夠複雜(App,FeedStory,Comment),那麼它就是一個可複用元件的候選項。function Comment(props) { return ( <div className="Comment"> <UserInfo user={props.author} /> <div className="Comment-text"> {props.text} </div> <div className="Comment-date"> {formatDate(props.date)} </div> </div> ); }
七、Props
- 上面在講元件定義時,我們已經提到過props屬性。當我們在渲染使用元件時無論是函數元件還是類元件,隻要給标簽元素添加了屬性,那麼這些屬性都會被轉換為props對象傳遞給函數或者類。
- 如果在渲染元件時使用的是雙閉合标簽,并且在标簽内部又嵌套了其它元件或添加了文本内容,則這些子元件或文本内容同樣會被轉換成普通對象或者是字元串并放置在props下的children屬性中
- 在元件中如果想擷取傳遞的屬性值或者子元件内容則可以直接通過props.[屬性名]或props.children.xxx擷取
function Welcome(props){ //通過props.name擷取傳遞的屬性值 return <h1>Hello, {props.name}</h1> } ReactDOM.render( <Welcome name="Alvin" />,//單閉合标簽,傳遞了name屬性 document.getElementById("root") );
下一章節中,我們将介紹一種新的概念,稱之為 “state”。在不違反上述規則的情況下,state 允許 React 元件随使用者操作、網絡響應或者其他變化而動态更改輸出内容。