何为组件?
组件就是让你可以独立于UI
的控件了,从某种意义上来看,组件的开发有点像javascript
函数式编程,也就是说组件像是个函数,输入一定的数据,然后处理完输出,将数据展现在视图中。
1.函数式组件(又称功能型组件)和类组件
看标题大家就知道函数式组件是什么鬼了,也就是组件的开发,简单地说就是写一个函数,如下:
function We(props) {
return <h1>Hello,{props.name}</h1>;
}
因为它遵循组件的特性,即输入一个props
对象作为参数,然后有返回一个JSX
对象,记住参数一定要为props
,返回值一定是JSX
对象哦,不然可不是函数式组件(稍微有点概念性的问题,尴尬)。
当然,重点戏到啦,那就是我们亲爱的ES6
的class
,这个class
来定义类和我们ES5
中定义对象其实是一样,只是换了一个方式,让对象创建更加普遍和通俗化,(我想想,后续我再补ES6
学习系列,简直happy happy)
class We extends React.Component {
render() {
return <h1>Hello,{this.props.name}</h1>;
}
}
从一定的观点上看,函数式组件和类组件是等价的
接下来我将会用函数式组件来讲解,因为类组件写麻烦一些,但是通过上述的描叙,大家应该可以非常轻松的将函数式组件改装为类组件
2.视图展示组件内容
在我前两篇博客中,我们只知道用DOM
元素标签来填充JSX
语句,就是div,img,p,h等等,如下:
const e1 = <div />;
const e2 = <img />;
const e3 = <p />;
如今学了上面两种组件方式后,我们终于可以解放啦,我们可以用组件来填充JSX语句
,用它来代替我们之前的DOM
节点,简直满满的幸福:
const el = <We name="tm7" />;
那我们的React
如何处理组件元素的呢?
当React
识别到了一个JSX
元素为一个已经定义的组件JSX
,它会通过JSX
属性将对应的props
对象传递给组件JSX
,如下:
function We(props) {
return <h1>Hello,{props.name}</h1>;
}
/** class We extends React.Component{ render(){ return <h1>hello,{this.props.name}</h1> } } **/
const el = <We name="tm7" />;
ReactDOM.render(
el,document.getElementById('root')
);
上述代码做了如下几件事情:
ReactDOM.render()
函数识别出JSX
为一个组件元素<We name="tm7" />
React
识别出组件元素中的props
对象{name:"tm7"}
,将props
对象传入组件元素- 我们的组件元素调用响应的视图渲染函数进行处理,如果是类组件则是调用
Render()
,如果是函数式组件则是调用函数本身,而返回值为<h1>hello,tm7</h1>
,其实返回的是一个JSX
对象 JSX
会被处理为DOM
节点,渲染在视图中
问题来啦,也就是说基本组件语法讲了,该注意的问题就需要提醒一下了
组件元素定义的JSX
必须是大写字母开头,也就是说<we/>
是错的,<We/>
才是对的,虽然我没有看过源码,但是就官方网站上的说明来看,React
识别是DOM
型JSX
还是组件型JSX
的区别就是开头字母有没有大写,如果大写了,就是组件,Reac
t就会去当前的作用域中寻找有没有相应的组件可以用。
3.构造复合组件
复合,就是融合多个组件,在一个组件中调用另外一些组件,获得那些组件的输出,然后再输出。
实例如下:
function We(props) {
return <h1>Hello,{props.name}</h1>;
}
function App() {
return (
<div>
<We name="tm7" />
<We name="jxy" />
<We name="cxl" />
</div>
);
}
ReactDOM.render(
<App />,document.getElementById('root')
);
知识题记
如果你想在一个已经存在的应用中增加别的组件的话,除了,前一篇文章说的:另选一个根节点可以重新处理(其实这种方式不算是增加到已有应用中)外,所谓的应用其实是一个大型JSX
组件标签,而所谓的增加额外组件就是在这个JSX
组件标签中增加其它的组件,从而形成一个应用。
这里还要说的是,每一个<script>
中都是一个独立的作用域
同时我们组件返回的一定是一个JSX
元素语句,而不是两个,实例如下:
//true
return (<div>
<h1>tm7</h1>
<h1>jxy</h1>
</div>
);
//false
return (
<h1>tm7</h1>
<h1>jxy</h1>
);
4.尽可能提纯我们的代码
首先我们要有个观念,不是越多的组件越好,也不是组件啥都没有就好了,适当的提取一些JSX
元素为一个组件元素会提高我们的代码的可读性和易用性。
实例如下:
function View(props) { return ( <div className="View"> <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="View-text"> {props.text} </div> <div className="View-date"> {props.date} </div> </div> ); } ReactDOM.render( <View text="tm7" date="new Date()" author={{name:'aaa',avatarUrl:'bbb'}}/>,document.getElementById('example') );
function Avatar(props) {
return (
<img className="Avatar"
src={props.user.avatarUrl}
alt={props.user.name}
/>
);
}
代码就变为了
function View(props) {
return (
<div className="View">
<div className="UserInfo">
<Avatar user={props.author} />
<div className="UserInfo-name">
{props.author.name}
</div>
</div>
<div className="View-text">
{props.text}
</div>
<div className="View-date">
{props.date}
</div>
</div>
);
}
我们接着提取
function UserInfo(props) {
return (
<div className="UserInfo">
<Avatar user={props.user} />
<div className="UserInfo-name">
{props.user.name}
</div>
</div>
);
}
代码又简化了
function View(props) {
return (
<div className="View">
<UserInfo user={props.author} />
<div className="View-text">
{props.text}
</div>
<div className="View-date">
{props.date}
</div>
</div>
);
}
提取行为的可用性和必要性
虽然提取过程是繁琐无聊的,但是当我们开发比较大的应用的时候,我们可能会重复大量相同的UI
,那么提取元素对我们开发大型应用和维护应用有着很大的帮助,同时,即便不是大型应用,当我们写一些较为复杂的组件时,提取也是有必要的,我们要让我们的组件更具可读性和重塑性,同时重复UI
必然存在,如此提取还是无法避免。
5.属性对象props
和只读性Read-Only
不管你是声明的函数式组件还是类组件,只要一定义,属性便不能改变,只能读取,也就是说,当声明一个组件后,对象是无法扩展的,不能对它增加属性。
实例如下:
var a = View({author:{name:'aaa',avatarUrl:'bbb'}});
a.tm = "tm7";
会报错:object is not extensible
,表示无法扩展。
同时对于props
的处理,由于props
是一个对象,大家可以用javascript
语法对他进行处理,即我们给JSX
元素进行属性设置时,可以直接看成给props
对象进行属性赋值。
同时这里涉及一个规范:javascript
函数式编程,下面是一个标准的简单函数式编程:
function sum(a,b) {
return a + b;
}
一般函数式编程评判的标准是,同一组输入,一定会产生同样的结果,不会对输入数据产生影响,这就是我们学数学知道的函数映射f(x)
,比如:二元一次方程,等等
如下则不是一个函数式编程
function wd(ac,am) {
retac.to -= am;
}
它会影响输入的数据,产生的结果也是不可测的
总的来说,React
是非常灵活的,但是也是很操蛋的,它有它的一套评判标准和严格的规范约束,官网上也提到过,所有的React
组件都必须是一个纯的函数,即基于函数式编程的函数,一种输入只会对应一种结果,同时不会影响输入的数据
下一篇博客将说说前一篇博客说到的,每次用
render
函数进行覆盖,多难受啊,放心,React
肯定给你解决方案啦,安啦,安啦