组件和属性
组件将UI分为独立、可重复利用的部分,且每一部分可以单独考虑。
概念上来讲组件就像JS函数:接受随机输入(叫做props),并返回React元素描述在屏幕上。
- 函数形式的组件和类形式的组件
最简单定义组件的方法是写一个函数。
function Welcome(props){ return <h1>hello,{props.name}</h1>; } |
这种函数是一个“空”React组件因为它接受一个单独的“props”数据对象参数并返回一个React 元素。
我们叫这种组件为“函数化”,因为它们就是一个JS函数
或者使用ES6class定义一个组件
Class Welcome extends React.Compoent{ Render(){ Return <h1>hellom{props.name}</h1>; }} |
这两种定义方式在React看来是等价的。但类定义的组件有一些额外的特性。
- 渲染一个组件
以前,我们遇到的React元素都是以DOM标签表示的。
const element = <div /> |
然而,元素也可以使用用户定义的组件来表示
const element = <Welcome name=”Sara” /> |
当React遇到一个元素代表用户定义的组件时,它将JSX属性作为一个单独的对象传递给这个组件,我们把这个对象叫做“props”
例如下面这个例子,它在网页上会显示出“Hello,Sara”
function Welcome(props){ return <h1>Hello,{props.name}</h1> } const element = <Welcome name=”Sara” /> ReactDOM.render( element, document.getElementById(‘root’) ); |
让我们看看这个例子里发生了什么:
1. 我们使用<Welcome name=’Sara’/>元素调用ReactDOM.render
2. React调用带有name参数作为props的welcome组件
3. 组件Welcome返回了<h1>Hello,Sara</h1>元素作为结果
4. ReactDOM更新了DOM来匹配这个元素
PS:组件总是以大写字母开头
- 构成组件
组件可以嵌套在其他组件中,也可以使用相同的组件抽象应用。包括按钮、表单、对话框等,都可以描述为组件。
例如,我们可以创建一个App组件多次渲染Welcome
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') ); |
这个Reactapps有一个单独的组件App在DOM树顶部。但如果你将它放入一个已经存在的APP中,它可能从最低端的小组件例如Button按钮开始逐渐渲染至视图层的顶端。
PS:组件必须返回一个单独的根元素。这就是为什么<div>要包含所有的<welcome>
- 组件分离
不要害怕将组件分离为更小的组件
考虑下面这个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> ); } |
它接受作者(一个对象),文本(一个字符串)和日期作为props来描述一个社交网站的评论
这个组件很难被重用,我们可以分离一些组件。
首先,我们可以分离Avatar
function Avatar(props) { return ( <img className="Avatar" src={props.user.avatarUrl} alt={props.user.name} /> ); } |
Avatar组件并不需要知道自己将要被渲染进Comment组件,这样做我们可以给她一个更一般的名字: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的一部分会被多次使用(例如按钮、面板、头像)或者app本身是一个相当复杂的应用(App、评论等),分离组件增加可重用性是一个bi比较
将组件中更小的组件分离出来形成可重用的组件。
- Props是只读的
无论组件的描述形式是怎样,都无法改变它的props。
function sum(a, b) { return a + b; } |
这类函数被称为纯函数,因为它不会尝试改变输入,且相同的输入会有相同的结果
相反,下面这类函数就会改变输入
function withdraw(account, amount) { account.total -= amount; } |
React有一个原则:
所有的React组件必须对其性质必须表现的像纯粹的函数一样:相同的输入有相同的输出
当然,应用程序UI是动态的、并且是根据时间变化的。下节会介绍状态这个概念,它允许React组件随着时间对用户行为、网络响应等作出反应改变输出,这并不违背上述原则。