react组件

学习react应该就是学习它的组件化开发了,看看它和vue的组件相比有何不同?

组件

react组件有两种,分别是函数定义和类定义。

函数定义组件

1
2
3
function Demo(){
return <h1>hello world</h1>
}

上面代码就创建了一个有效的组件,它是用函数返回了一个React元素,这种类型的组件为函数定义组件,我们可以正常使用它:

1
2
3
4
ReactDOM.render(
<Demo/>,
app
)

记住ReactDOM.render()的第一个参数是要渲染的内容,组件不就是要展示的内容吗?只是组件的使用应写成标签形式,这点应该很熟悉,跟vue的一样。

这里有一个特别需要注意的地方,那就是组件名称必须以大写字母开头,不然无法渲染。

看到上面的代码,不知道你会不会有一种想法,一种下意识的想法,看代码:

1
2
3
4
5
function Demo(){
return <h1>hello world</h1>
}

ReactDOM.render(Demo(),app)

看到了吗?我不把Demo写成标签形式,而是直接调用了,结果依然正确渲染了。细想下,这本该如此啊,因为函数返回的是JSX啊,这与直接写JSX并没有本事区别。此时,你要是把函数名写成小写也不会有影响了,因为它已经不是一个组件了:

1
2
3
4
5
function demo(){
return <h1>hello world</h1>
}

ReactDOM.render(demo(),app)

类定义组件

相对于函数定义组件,类定义组件才是最常用的,因为它允许我们更多的扩展。

类定义组件,其实就是es6中定义类的方法,只是react加入了一些东西:

1
2
3
4
5
6
7
class Demo extends React.Component{
render(){
return <h1>hello world</h1>
}
}

ReactDOM.render(<Demo/>,app)

上面就是类定义组件,熟悉es6的话,应该比较熟悉这种形式,在定义一个组件时,我们必须让它继承React.Component这个类,这样才能使用react的一些功能。

关于类定义组件,要讲得东西其实还是有几个的,在这之前,我们要了解两个和组件息息相关的概念:属性和状态。

组件的属性和状态

组件有属性和状态,分别表示为props和state,它们的区别:

  • 属性是由父组件传递给子组件的
  • 状态是子组件内部维护的数据,当状态发生变化的同时,组件也会进行更新。当状态发生转换时会触发不同的钩子函数,从而让开发者有机会做出相应

属性(props)

属性是写在父组件上的,可以传递给子组件:

1
2
3
4
function Demo(props){
return <h1>{props.msg}</h1>
}
ReactDOM.render(<Demo msg='hello world'/>,app)

上面是函数定义组件的属性传递写法,Demo函数接收一个单一的“props”对象,这里包含了所有的父组件的属性,我们多写几个属性,打印props看看:

1
2
3
4
5
6
7
8
9
10
11
12
13
function Demo(props){
console.log(props)
return <h1>{props.msg}</h1>
}
ReactDOM.render(<Demo msg='hello world' name='wj' age='18'/>,app)


//props打印结果
{
age:"18",
msg:"hello world",
name:"wj"
}

需要注意的是,在react里要输出数据到模板里,用一个花括号即可,在vue里是两个花括号。

属性不仅仅可以是字符串,还可以是js的表达式:

1
2
const num = 5
ReactDOM.render(<Demo msg={num>3?'大于3':'小于3'} name='wj' age='18'/>,app)

这里就不多说了,react相对于vue,做了更少的限制,大胆去尝试吧,很多你觉得可以的,在react确实可以做到。

再来看看在类定义组件里使用props:

1
2
3
4
5
6
7
class Demo extends React.Component{
render(){
return <h1>{this.props.msg}</h1>
}
}

ReactDOM.render(<Demo msg='hello world' />,app)

这里使用props的唯一区别就是要使用this来调用,其他跟函数定义组件一样。

props有一个限制,那就是它的只读性,无论是使用函数或是类来声明一个组件,它决不能修改它自己的props。如果需要改变props值来更新视图显然是不行的。这就需要state状态。

状态(state)

状态适用于类定义的组件,状态是私有的,完全受控于当前组件。我们需要使用状态,则需要定义类组件。

1
2
3
4
5
6
7
8
9
10
11
12
13
class Demo extends React.Component{
constructor(){
super()
this.state={
msg:'hello world'
}
} // 注意这里不要写逗号,详见es6之class
render(){
return <div>{this.state.msg}</div>
}
}

ReactDOM.render(<Demo/>,app)

上面代码添加了状态,在这里你可能需要知道一些es6的class知识,推荐阮一峰老师的es6教程。我们知道Demo类式继承React.Component这个类的,算子类,它必须在constructor方法中调用super方法,否则新建实例时会报错。这是因为子类自己的this对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,加上子类自己的实例属性和方法。如果不调用super方法,子类就得不到this对象。

在this对象上添加状态state,这个状态是属于Demo私有的,使用this.state.xxx的方式调用。

状态受控于当前组件,是可以改变的,这里我们来改变下状态,注意这里会使用到事件,以后会详细介绍react事件:

1
2
3
4
5
6
7
8
9
10
change(){
this.state.msg = '改变了'
}

render(){
return <div>
<input type='button' value='改变' onClick={this.change.bind(this)} /><br/>
<p>{this.state.msg}</p>
</div>
}

上面代码中,当我们点击按钮时,state数据会改变,同时视图也会更新。但是当你运行后,会发现视图并不会更新。原因在哪里?

其实问题就出在了this.state上,我们不能直接使用this.state来变更状态,这样不会重新渲染组件,我们需要使用setState()方法来更新状态:

1
2
3
4
5
6
7
change(){
//this.state.msg = '改变了'

this.setState({
msg:'改变了'
})
}

这样,当状态更新后,组件才会重新渲染。

总结

我也不知道怎么总结了,很多细节我也没说到,这算是自己的一个回顾吧。与vue相比,react似乎限制地更少,这也意味着我们可以更灵活地组织我们的代码。学习过程中应该大胆尝试,你想到的,react作者们可能也想到了。另外在vue里,组件不是必须的,更多的关注点是在数据层上,但在react里,组件是不可缺少的,一切开发都是基于组件的(至少现在我是这么觉得),这也反映了两大框架的不同思想吧,不管如何,多学学总不会差。