組件允許你將 UI 拆分為獨(dú)立可復(fù)用的代碼片段,并對(duì)每個(gè)片段進(jìn)行獨(dú)立構(gòu)思。
組件,從概念上類(lèi)似于 JavaScript 函數(shù)。它接受任意的入?yún)ⅲ?“props”),并返回用于描述頁(yè)面展示內(nèi)容的 React 元素。
定義組件最簡(jiǎn)單的方式就是編寫(xiě) JavaScript 函數(shù):
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
該函數(shù)是一個(gè)有效的 React 組件,因?yàn)樗邮瘴ㄒ粠в袛?shù)據(jù)的 “?props
?”(代表屬性)對(duì)象與并返回一個(gè) React 元素。
這類(lèi)組件被稱為“函數(shù)組件”,因?yàn)樗举|(zhì)上就是 JavaScript 函數(shù)。
你同時(shí)還可以使用 ES6 的 class 來(lái)定義組件:
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
上述兩個(gè)組件在 React 里是等效的。
之前,我們遇到的 React 元素都只是 DOM 標(biāo)簽:
const element = <div />;
不過(guò),React 元素也可以是用戶自定義的組件:
const element = <Welcome name="Sara" />;
當(dāng) React 元素為用戶自定義組件時(shí),它會(huì)將 JSX 所接收的屬性(attributes)以及子組件(children)轉(zhuǎn)換為單個(gè)對(duì)象傳遞給組件,這個(gè)對(duì)象被稱之為 “props”。
例如,這段代碼會(huì)在頁(yè)面上渲染 “Hello, Sara”:
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
const element = <Welcome name="Sara" />;
ReactDOM.render(
element,
document.getElementById('root')
);
讓我們來(lái)回顧一下這個(gè)例子中發(fā)生了什么:
注意: 組件名稱必須以大寫(xiě)字母開(kāi)頭。
React 會(huì)將以小寫(xiě)字母開(kāi)頭的組件視為原生 DOM 標(biāo)簽。例如,代表 HTML 的 div 標(biāo)簽,而則代表一個(gè)組件,并且需在作用域內(nèi)使用 Welcome。
組件可以在其輸出中引用其他組件。這就可以讓我們用同一組件來(lái)抽象出任意層次的細(xì)節(jié)。
按鈕,表單,對(duì)話框,甚至整個(gè)屏幕的內(nèi)容:在 React 應(yīng)用程序中,這些通常都會(huì)以組件的形式表示。
例如,我們可以創(chuàng)建一個(gè)可以多次渲染 Welcome 組件的 App 組件:
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
function App() {
return (
<div>
<Welcome name="Sara" />
<Welcome name="Cahal" />
<Welcome name="Edite" />
</div>
);
}
ReactDOM.render(
<App />,
document.getElementById('root')
);
通常來(lái)說(shuō),每個(gè)新的 React 應(yīng)用程序的頂層組件都是 App 組件。
但是,如果你將 React 集成到現(xiàn)有的應(yīng)用程序中,你可能需要使用像 Button 這樣的小組件,并自下而上地將這類(lèi)組件逐步應(yīng)用到視圖層的每一處。
將組件拆分為更小的組件。
例如,參考如下 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>
);
}
該組件用于描述一個(gè)社交媒體網(wǎng)站上的評(píng)論功能,它接收 author(對(duì)象),text (字符串)以及 date(日期)作為 props。
該組件由于嵌套的關(guān)系,變得難以維護(hù),且很難復(fù)用它的各個(gè)部分。因此,讓我們從中提取一些組件出來(lái)。
首先,我們將提取 Avatar 組件:
function Avatar(props) {
return (
<img className="Avatar" src={props.user.avatarUrl} alt={props.user.name} /> );
}
Avatar 不需知道它在 Comment 組件內(nèi)部是如何渲染的。因此,我們給它的 props 起了一個(gè)更通用的名字:user,而不是 author。
我們建議從組件自身的角度命名 props,而不是依賴于調(diào)用組件的上下文命名。
我們現(xiàn)在針對(duì) Comment 做些微小調(diào)整:
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>
);
}
接下來(lái),我們將提取 UserInfo 組件,該組件在用戶名旁渲染 Avatar 組件:
function UserInfo(props) {
return (
<div className="UserInfo">
<Avatar user={props.user} />
<div className="UserInfo-name"> {props.user.name} </div>
</div>
);
}
進(jìn)一步簡(jiǎn)化 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>
);
}
最初看上去,提取組件可能是一件繁重的工作,但是,在大型應(yīng)用中,構(gòu)建可復(fù)用組件庫(kù)是完全值得的。
根據(jù)經(jīng)驗(yàn)來(lái)看,如果 UI 中有一部分被多次使用(Button,Panel,Avatar),或者組件本身就足夠復(fù)雜(App,F(xiàn)eedStory,Comment),那么它就是一個(gè)可復(fù)用組件的候選項(xiàng)。
組件無(wú)論是使用函數(shù)聲明還是通過(guò) class 聲明,都決不能修改自身的 props。來(lái)看下這個(gè) sum 函數(shù):
function sum(a, b) {
return a + b;
}
這樣的函數(shù)被稱為“純函數(shù)”,因?yàn)樵摵瘮?shù)不會(huì)嘗試更改入?yún)?,且多次調(diào)用下相同的入?yún)⑹冀K返回相同的結(jié)果。
相反,下面這個(gè)函數(shù)則不是純函數(shù),因?yàn)樗牧俗约旱娜雲(yún)ⅲ?/p>
function withdraw(account, amount) {
account.total -= amount;
}
React 非常靈活,但它也有一個(gè)嚴(yán)格的規(guī)則:
所有 React 組件都必須像純函數(shù)一樣保護(hù)它們的 props 不被更改。
更多建議: