React :元素构成组件(复杂的组件由多个组件构成),组件又构成应用。
React核心思想是组件化,其中 组件 通过属性(props) 和 状态(state)传递数据。
一、什么是组件?
React通过组件的思想,将界面拆分成一个个可复用
的模块,每一个模块就是一个React 组件。一个React 应用由若干组件组合而成,一个复杂组件也可以由若干简单组件组合而成。
React 组件可以用好几种方式声明,可以是一个包含 render() 方法的类,也可以是一个简单的函数,不管怎么样,它都是以props作为输入,返回 React 元素作为输出。
二、组件的作用
React组件最核心的作用是返回React元素。
这里你也许会问:React元素不应该是由React.createElement() 返回的吗?
其实React组件就是调用React.createElement(),返回React元素,供React内部将其渲染成最终的页面DOM。
下面看两种创建组件的方式:
//函数式
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
//类定义
class Welcome extends React.Component {
//render函数并不做实际的渲染动作,他只是返回一个JSX
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
无论是函数式组件,还是类定义组件,最终组件return的都是React元素,而return的React元素(JSX)则又调用了 React.createElement()
从return React元素到组件被实际渲染 挂到DOM树上 中间还有很复杂的过程。
比如:在类组件中render函数被调用完之后,componentDidMount函数并不是会被立刻调用。componentDidMount被调用的时候,render函数返回的东西已经引发了渲染,组件已经被『装载』到了DOM树上
其实,使用类定义的组件,render方法是唯一必需的方法,其他组件的生命周期方法都只不过是为render服务而已
,都不是必需的。
三、创建组件
组件是由元素构成的。元素数据结构是普通对象,而组件数据结构是类或纯函数。
React 中有三种构建组件的方式:
- 类 组件:class extends React.Component()
- js函数式组件
- React.createClass()
1、类 组件
相比于函数式组件功能更强大。它有state,以及不同的生命周期方法,可以让开发者能够在组件的不同阶段(挂载、更新、卸载),对组件做更多的控制。
类组件可能是无状态组件,也可能是有状态组件。详见:组件分类
//组件名首字母必须大写
class Welcome extends React.Component{
//添加其他事件函数这么加:myWay(){···}
render(){
return (<div>my name is {this.props.name},{this.props.age}</div>);
}
};
2、函数式组件
JavaScript 函数构建的组件一定是无状态
组件。它能传入props和context两个参数,没有state
,除了render(),没有其它生命周期方法。
但正是这样,函数组件才更加专注和单一,它只是一个返回React 元素的函数,只关注对应UI的展现。函数组件接收外部传入的props,返回对应UI的DOM描述。
//组件名称总是以大写字母开始。
//定义模版的函数名首字母必须大写
function Welcome(props){
//添加其他事件函数这么加:function otherFun(){·····}
return <div>my name is {props.name},{props.age}</div>;
}
3、React.createClass()
/*组件首字母必须大写,eg:Greeting的G*/
var Greeting = React.createClass({
render: function() {
return <h1>Hello, {this.props.name}</h1>;
}
});
创建组件时需注意:
1、给组件命名时,组件首字母必须大写
2、render()方法的return 里,只能有一个html父标签
,所以涉及到多个html标签时,必须外面再嵌套个父标签
四、渲染一个组件
1、类 组件
//组件名首字母必须大写
class Welcome extends React.Component{
render(){
return (<div>my name is {this.props.name},{this.props.age}</div>);
}
};
//const element必须在定义组件之后
const element = (
<Welcome name='bty' age="12"/>
);
ReactDOM.render(
element,
document.getElementById('root')
);
输出:my name is bty,12
2、js函数式组件
function Welcome(props){
return <div>my name is {props.name},{props.age}</div>;
}
const element = (
<Welcome name='bty' age='12'/>
);
ReactDOM.render(
element,
document.getElementById('root')
);
输出:同上
工作步骤:
(1) 调用ReactDOM.render(),并向其中传入
< Welcome name=’bty’ age=’12’/>元素。
(2) React 调用 Welcome 组件,并向其中传入了 {name: ‘bty’,age=’12’} 作为 props 对象
(3) Welcome 组件返回 < div>my name is {props.name},{props.age}
(4) React DOM 迅速更新 DOM ,使其显示为 < div>my name is bty,12
五、组件嵌套
function Myhi(props){
return <h1>I‘m {props.name}</h1>
}
class Welcome extends React.Component{
render(){
return(
<div>
<p>start</p>
<Myhi name="bty"/>
<Myhi name="ssh"/>
<Myhi name="mama"/>
</div>)
}
};
ReactDOM.render(
<Welcome/>,
document.getElementById('root')
);
输出:
start
I‘m bty
I‘m ssh
I‘m mama
组件嵌套是如何渲染的?
拿上面例子说,
(1)首先调用ReactDOM.render(),向其中传入< Welcome/>组件;
(2)渲染时:组件< Welcome/>知道如何渲染< p>标签,但不知道如何渲染< Myhi/>,所以再继续调用解析对应组件< Myhi/>,并以{name:'···'}作为props对象传给Myhi组件,返回React 元素
,
(3)像上面这样一直做递归,直到返回的React 元素中只包含DOM节点为止
。
这样React 就能获取到页面的完整DOM结构信息,渲染的工作自然就ok了。
六、组件的实例
React 组件是一个函数或类,实际工作时,发挥作用的是React 组件的实例对象。只有组件实例化后,每一个组件实例才有了自己的props和state,才持有对它的DOM节点和子组件实例的引用。
在传统
的面向对象的开发
方式中,实例化的工作是由开发者自己手动完成
的,但在React中
,组件的实例化
工作是由React自动完成
的,组件实例也是直接由React管理的。换句话说,开发者完全不必关心组件实例的创建、更新和销毁。
在类组件中 的实例,就是用 this 引用的那个对象
,对于保存本地状态以及介入生命周期函数是有用的。
注:组件必须是 纯函数
无论你用函数或类的方法来声明组件, 它都无法修改其自身 props. 思考下列 sum (求和)函数:
function sum(a, b) {
return a + b;
}
这种函数称为 “纯函数” ,因为它们不会试图改变它们的输入,并且对于同样的输入,始终可以得到相同的结果。
反之, 以下是非纯函数, 因为它改变了自身的输入值:
function withdraw(account, amount) {
account.total -= amount;
}
严格的规则:
所有 React 组件都必须是纯函数,并禁止修改其自身 props