在React组件化开发中,把组件分为函数组件、类组件、Hooks组件
,这里本文先认识函数组件和类组件。
函数组件
使用 JS 函数(普通,箭头)创建的组件,就是函数组件,函数组件也称“静态组件”。
定义函数组件
- 函数名称首字母必需大写,React 据此来区分组件和 HTML 元素
- 函数必须有返回值,表示该组件的 UI 结构,如果不渲染任何内容可返回null
先来看看函数组件长啥样,看下面代码:
// 普通函数
function Header() {
return <div>头部组件</div>;
}
// 箭头函数
const Footer = () => {
return <div>底部组件</div>;
};
// 不渲染内容
const Loading = () => {
const loading = false;
return loading ? <div>正在加载...</div> : null;
};
上面创建的函数Header、Footer、Loading
就是一个个函数组件了。那要怎么使用呢?
使用函数组件
// 创建一个Child子组件
function Child(props){
let { title } = props
return <div className="child">
<h2>{title}</h2>
</div>
}
// 父组件使用
function App(){
return <Child title='用户投票数量'></Child>
}
以上函数的名称(Child
)就是组件名称,使用组件就是把组件名称当标签使用即可。
静态组件
那函数组件为什么又称“静态组件”呢?看下面这段代码:
function Child(props){
let { title } = props
let name = '小浩', num = 0
return <div className="child">
<h2>{title}</h2>
<ul>
<li>姓名:{name}</li>
<li>票数:{num}</li>
</ul>
<div className="footer">
<button onClick={() => {
num ++
console.log('num-----',num)
}}>投票</button>
</div>
</div>
}
浏览器效果:
以上当我们点击投票时,改变num
的值,可以看到num
在控制台输出的变化,但页面却没有更新视图。
综合以上,也就是函数组件中的内容,不会根据组件内的某些操作进行更新试图,所以称它为静态组件
。除非在父组件中,有数据更新,那么对应的子组件也会更新。
类组件
使用class
语法创建的组件就是类组件,类组件也称“动态组件”。
定义类组件
- 类名首字母必需大写
- 必须继承
React.Component
或PureComponent
这个类 - 必须给当前类设置一个
render
的方法,这个方法是放在其原型上的,在render
方法中,返回需要渲染的视图,无渲染可返回null
import React from 'react';
class Child extends React.Component{
render(){
let { title } = this.props
return <div className="child">
<h2>{title}</h2>
</div>
}
}
export default Child
上面代码是用class
创建的类Child
且基于extends
继承React.Component
,在Child
类里设置了一个render
的方法,然后返回需要渲染的视图,这样的组件就是类组件了。
动态组件
那类组件为什么又称“动态组件”呢?看下面这段代码:
class Child extends React.Component{
name = '小浩'
num = 0
render(){
let { title } = this.props
return <div className="child">
<h2>{title}</h2>
<ul>
<li>姓名:{this.name}</li>
<li>票数:{this.num}</li>
</ul>
<div className="footer">
<button onClick={() => {
this.setState({
num:this.num ++
})
console.log('num-----',this.num)
}}>投票</button>
</div>
</div>
}
}
浏览器效果:
以上当我们点击投票时,改变num
的值,可以看到num
在控制台输出的变化,但页面也随着更新视图。
综合以上,也就是类组件中的内容,会根据组件内的某些操作改变状态,当状态改了,浏览器里视图也更新了,所以称这样的组件为动态组件
。在上面用来改变num
值的方法this.setState
是React.Component.prototype
原型上提供的方法,后面会说到。
class类组件做了些什么
创建new Child()
实例,类组件内部发生了什么?下面来看看这段代码:
class Child extends React.Component{
name = '小浩'
age = 30
work = ()=>{
console.log('程序员')
}
run(){
console.log('跑步')
}
render(){
let { title } = this.props
return <div className="child">
<h2>{title}</h2>
</div>
}
}
//创建实例p
const p = new Child()
console.log(p)
上面添加的name、age、work
是Child
的私有属性,而添加的run
方法是Child.prototype
上的方法。
由于Child
是extends
实现的继承,所以有以下特点:
1、先是基于call
继承,相当于执行了React.Component.call(this)
,把this
指向了Child
类的实例p
也就是执行了这个Component
函数。在执行了React.Component.call(this)
后给创建的实例p
设置了四个私有属性: props、context、refs、updater
。
2、再基于原型继承:Child.prototype.__proto__ == React.Component.prototype
这样实例可以沿着原型链访问:实例 -> Child.prototype -> React.Component.prototype -> Object.prototype
,实例除了具备Child.prototype
提供的方法之外,还具备React.Component.prototype
原型上提供的方法: isReactComponent、setState、 forceupdate
3、构造函数constructor
既然是类组件,那么在类组件里面就会存在一个构造函数constructor
,这个函数可写可不写,只有需要接受传递进来的实参信息时,才需要设置constructor
。如果写了constructor
那么在函数里一定要写super()
方法。
class Child extends React.Component{
constructor(props){
super(props)
}
render(){
let { title } = this.props
return <div className="child">
<h2>{title}</h2>
</div>
}
}
上面的super(props)
就等价于React.Component.call(this)
,在执行了super(props)
后Child
就增加了四个私有属性: props、context、refs、updater
,它们当前的状态分别是 :
this.props=props
this.context=undefined
this.refs={}
this.updater=(...}
使用类组件
创建类组件Child
import React from 'react';
class Child extends React.Component{
render(){
let { title } = this.props
return <div className="child">
<h2>{title}</h2>
</div>
}
}
export default Child
父组件使用Child
组件
function App(){
return <div>
<Child title='用户投票数量1'></Child>
<Child title='用户投票数量2'></Child>
</div>
}
上面使用了两次Child
类组件,相当于创建了两个实例,如下:
new Child({
title:'用户投票数量1'
})
new Child({
title:'用户投票数量2'
})
由此可知,同一个组件多次调用,互相之间不会产生直接的影响,因为每次调用都是创建的一个单独的实例。