web前端高階React - React從入門到出門之元件化開發及Props屬性傳值
技術標籤:ReactWEB前端框架component元件化開發props函式元件類元件
系列文章目錄
第一章:React從入門到出門之初識React
第一章:React從入門到出門之JSX簡介
第三章:React從入門到出門之元素渲染
第四章:React從入門到出門之JSX虛擬DOM渲染為真實DOM的原理和步驟
第五章:React從入門到出門之元件化開發及Props屬性傳值
文章目錄
一、React元件的定義及分類
- 元件允許我們可以將UI拆分成一個個獨立的可複用的程式碼片段,並且可以對每個片段進行獨立構思和管理。
- 從概念上講,元件類似於JavaScript中的函式,它接收任意的輸入引數(props),並返回用於描述頁面展示內容的React元素
- React 元件可以定義為類(class)元件或函式(function)元件的形式
二、函式式元件
- 函式式元件其實就是寫一個JavaScript中的函式,接收一個props物件作為引數,並返回用於描述頁面內容的React元素(JSX語法),這就構成了一個函式式元件。
- 需要注意的是:元件名稱必須以大寫字母開頭,因為React 會將以小寫字母開頭的元件視為原生 DOM 標籤。例如,< div /> 代表 HTML 的 div 標籤,而 < Clock/> 則代表一個元件,並且需在作用域內使用 Clock。
- 函式式元件屬於靜態元件,調取元件渲染出的結果,除非重新渲染元件,否則第一次渲染的內容不會改變(但可以通過React HOOKS解決)
下面我們以一個時鐘元件為例編寫一個函式式元件
function Clock(props){ return <div>{new Date().toLocaleString()}</div> }
上面程式碼就是一個有效的React元件,因為它接收唯一帶有資料的 “props”(代表屬性)物件與並返回一個 React 元素(JSX語法),這類元件就被稱為“函式元件”,因為它本質上就是一個JavaScript函式。
三、類元件
- 上面我們已經說過,定義一個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)來進行操作
function Welcome(props){ return <h1>Hello, {props.name}</h1> } ReactDOM.render( <Welcome name="Alvin" />,//單閉合標籤 document.getElementById("root") );
上述程式碼執行結果,會在頁面上顯示 “Hello, Alvin”,Alvin就是我們通過name屬性動態傳遞給Welcome元件的
- 上面的例子中,我們通過呼叫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> ); }
- 該元件用於描述一個社交媒體網站上的評論功能,它接收 author(物件),text (字串)以及 date(日期)作為 props。
- 該元件由於巢狀的關係,變得難以維護,且很難複用它的各個部分。因此,讓我們從中提取一些元件出來。
首先,我們將提取 Avatar 元件:
function Avatar(props) { return ( <img className="Avatar" src={props.user.avatarUrl} alt={props.user.name} /> ); }
- Avatar 不需知道它在 Comment 元件內部是如何渲染的。因此,我們給它的 props 起了一個更通用的名字:user,而不是 author。
- 我們建議從元件自身的角度命名 props,而不是依賴於呼叫元件的上下文命名。
我們現在針對 Comment 做些微小調整: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> ); }
接下來,我們將提取 UserInfo 元件,該元件在使用者名稱旁渲染 Avatar 元件:
function UserInfo(props) { return ( <div className="UserInfo"> <Avatar user={props.user} /> <div className="UserInfo-name"> {props.user.name} </div> </div> ); }
進一步簡化 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> ); }
最初看上去,提取元件可能是一件繁重的工作,但是,在大型應用中,構建可複用元件庫是完全值得的。根據經驗來看,如果 UI 中有一部分被多次使用(Button,Panel,Avatar),或者元件本身就足夠複雜(App,FeedStory,Comment),那麼它就是一個可複用元件的候選項。
七、Props 的只讀性
- 上面在講元件定義時,我們已經提到props是隻讀的不能被修改的,但是應用程式的UI應該是動態的,並會伴隨著時間的推移而變化。
- 下一章節中,我們將介紹一種新的概念,稱之為 “state”。在不違反上述規則的情況下,state 允許 React 元件隨使用者操作、網路響應或者其他變化而動態更改輸出內容。