以下代码,没有使用模块化的方式,使用的是CDN的方式。如果需要源代码,请从这个地址下载:
```text
链接:https://pan.baidu.com/s/1s57mr5AE_ecWBFZ5TJTwqw 提取码:f7x2
```
另外,这篇文章,主要是剖析**组件的初次渲染和重新渲染**。所以,其它部分不要太较劲。
### **一、react类组件和函数式组件重新渲染时的区别**
### **1、看现象:**
### **1)代码(demo01)**
**类组件:**
```js
// 1、类组件
class ComClass extends React.Component {
constructor(props) {
super();
this.props = props;
console.log("类组件的构造函数被调用了");
}
render() {
console.log("类组件的render被调用了");
return (
<div style={{ "backgroundColor": "pink" }}>
<h5 >我是类组件</h5>
<p>{this.props.name}</p>
</div>
);
}
}
```
**函数式组件:**
```js
// 2、函数式组件
function ComFn(props) {
console.log("函数式组件的函数被调用了");
return (
<div style={{ "backgroundColor": "skyblue" }}>
<h5 >我是函数式组件</h5>
<p>{props.name}</p>
</div>
);
}
```
**使用组件:**
```js
let name = "张三疯";
function changeName() {
name = "张四疯"
renderDom();
}
function renderDom() {
ReactDOM.render(
<div>
<button onClick={changeName} >修改</button>
<ComClass name={name} /><br />
<ComFn name={name} />
</div>, document.getElementById("box"));
}
renderDom();
```
### **2)运行:**
2.1)初次运行时,我们发现在控制台中打印的内容为:
```js
类组件的构造函数被调用了
类组件的render被调用了
函数式组件的函数被调用了
```
2.2)当点击“修改”按钮时,我们发现控制台中打印的内容为:
```js
类组件的render被调用了
函数式组件的函数被调用了
```
### **3)总结(敲黑板,重点):**
1、类组件重新渲染时,只调用了render
2、函数式组件重新渲染时,会调用函数整个本身(哈哈,不调用它也不行啊)
### **2、看原理:**
### **1)用原生的方式剖析:**
函数式组件的剖析:
```js
标签的方式使用函数式组件:
<ComFn name={name} />
基本上等价于:
{ComFn({name})} //组件的方式使用,就是在调用函数
```
类组件的剖析:
```js
标签的方式使用类组件:
<ComClass name={name} /><br/>
等价于
{new ComClass({name}).render()}
但是
这样改了后,初次渲染没有问题。当点击了“修改”按钮时,不一样了。
所以,类组件里,应该是把new ComClass({name})实例化出来的对象,记录起来了。所以,应该等价于
1、定义一个对象,保存了new ComClass({name})
//在react里对类组件对象进行了保存。
let comObj = new {new ComClass({name});
2、在渲染时,只是调用了类组件的render函数。
comObj.render();
```
即:最终代码变成了如下:
### **2)代码(demo02):**
类组件和函数式组件的代码不用改变。
**使用组件**
```js
let name = "张三疯";
//此处保存了类组件的实例化对象(这个只是模拟实现,react内部并不是这么简单)
let comObj = new ComClass({name});
function changeName(){
name = "张四疯";
comObj.props = {name}
renderDom();
}
function renderDom(){
ReactDOM.render(
<div>
<button onClick={changeName} >修改数据</button>
{/*此处用原生的方式使用组件*/}
{comObj.render()}
{ComFn({name})}
</div>, document.getElementById("box"));
}
renderDom();
```
### **3)运行:**
3.1)、初次运行时,我们发现在控制台中打印的内容为:
```js
类组件的构造函数被调用了
类组件的render被调用了
函数式组件的函数被调用了
```
3.2)、当点击“修改”按钮时,我们发现控制台中打印的内容为:
```js
类组件的render被调用了
函数式组件的函数被调用了
```
> 运行结果和组件的方式一样。
### **二、看看组件里使用定时器,并且,重新渲染组件**
### **1、看现象**
### **1)代码(demo03):**
**类组件:**
```js
// 1、类组件
class ComClass extends React.Component {
constructor(props) {
super();
this.props = props;
console.log("类组件的构造函数被调用了");
}
showMessage = () => {
//在显示props时(通过this访问props),props里的内容被改变了。
console.log('类组件: ' + this.props.name);
};
handleClick = () => {
// 分析问题:
// 1、3秒钟后调用函数(通过 this 的方式调用)
setTimeout(this.showMessage, 3000);
};
render() {
console.log("类组件的render被调用了");
return (
<div style={{ "backgroundColor": "pink" }}>
<h5 >我是类组件</h5>
<p>name:{this.props.name}</p>
<input type="button" value="调用带着定时器的函数" onClick={this.handleClick} />
</div>
);
}
}
```
**函数式组件:**
```js
// 2、函数式组件
function ComFn(props) {
console.log("函数式组件的函数被调用了");
//这个是闭包:
// 每调用一次父函数(ComFn),都会重新定义一个新的子函数。新的函数中保存着父函数新的形参
const showMessage = () => {
console.log('函数式组件: ' + props.name);
};
//这个是闭包:
//每调用一次父函数(ComFn),都会重新定义一个新的子函数。新的函数中保存着父函数新的形参
const handleClick = () => {
setTimeout(showMessage, 3000);
};
return (
<div style={{ "backgroundColor": "skyblue" }}>
<h5 >我是函数式组件</h5>
<p>name:{props.name}</p>
{/*先点击这个按钮,调用,第一次定义的 showMessage和handleClick*/}
<input type="button" value="调用带着定时器的函数" onClick={handleClick} />
</div>
);
}
```
**使用组件:**
```js
let name = "张三疯";
function changeName() {
name = "张四疯"
renderDom();
}
function renderDom() {
ReactDOM.render(
<div>
<button onClick={changeName} >修改</button>
<ComClass name={name} /><br />
<ComFn name={name} />
</div>, document.getElementById("box"));
}
renderDom();
```
### **2)、运行:**
**2.1)类组件:**
点击类组件的“调用带着定时器的函数” 按钮后,再点击”修改“按钮(**注意先后顺序**)。我们发现了:界面上打印的和控制台打印的是**一样的**。这是**不对的**:因为,我点击“调用带着定时器的函数” 按钮时,name的值是”张三疯“,应该打印”张三疯“
**2.2)函数式组件:**
点击类组件的“调用带着定时器的函数” 按钮后,再点击”修改“按钮(**注意先后顺序**)。我们发现了:界面上打印的和控制台里打印的**不一样**,这是**对的**:因为,我点击“调用带着定时器的函数” 按钮时,name的值是”张三疯“,应该打印”张三疯“
### **3)总结**
原因何在?以下文字要细细的品,如果品不出来,就结合上面的代码,再看看。
类组件:
当**类组件重新渲染**时,**只调用了render函数**。组件的this不变。等定时器到了时,读取属性的的值时,先通过this找到props。再通过props找到name。而此时,name的值,已经发生了变化。所以,自然读到的是新值“张四疯” ,这个应该好理解。
函数式组件:
(你必须对闭包是清楚的),当**函数式组件重新渲染**时,**调用了函数**(组件),那么在函数式组件里的 函数(showMessage,handleClick)就会被重新定义,新定义的函数保存着父函数(组件)的新的形参和局部变量。而我们点击“调用带着定时器的函数”时,调用的是 第一次定义的showMessage,handleClick(这两个函数里保存了父函数(组件)第一次传入的形参和局部变量)。
其实,上面的代码中,已经做了注释。我再次解释后,如果不明白的话,看看下面的剖析原理的代码。
### **2、看原理:**
### **1)用原生的方式剖析**
与第一大点的第二小点的“剖析原理”一样:
函数式组件的剖析:
```js
标签的方式使用函数式组件:
<ComFn name={name} />
基本上等价于:
{ComFn({name})} //组件的方式使用,就是在调用函数
```
类组件的剖析:
```js
把new ComClass({name})实例化出来的对象,记录起来了。
1、定义一个对象,保存了new ComClass({name})
//在react里对类组件对象进行了保存。
let comObj = new {new ComClass({name});
2、在渲染时,只是调用了类组件的render函数。
comObj.render();
```
### **2)代码(demo04):**
类组件和函数式组件的代码不用变(同第二大点的第一小点:组件里使用定时器,并重新渲染组件)。
**使用组件:**
这个代码等同于第一大点的第二小点。
```js
let name = "张三疯";
let comObj = new ComClass({ name });
function changeName() {
name = "张四疯";
comObj.props = { name }
renderDom();
}
function renderDom() {
ReactDOM.render(
<div>
<button onClick={changeName} >修改</button>
{comObj.render()}
{ComFn({name})}
</div>, document.getElementById("box"));
}
renderDom();
```
### **三、为什么react现在更推荐函数式组件**
React的核心理念之一就是,**界面应当是数据的不同形式的简单投影**。**相同的输入应该产生相同的输出**。而函数式组件的写法,使用闭包的特性,显然符合这一理念:每个闭包里保存在父函数的当前形参(props)和局部变量。而类组件里,由于,每次读取数据,要根据this指针去读取,那必然不会读取到属于自己当前状态的值。而是更新后的最新的值。
### **四、补充:类组件如何解决以上问题呢:**
其实还是利用了闭包。
### **看类组件的代码修改(demo05):**
```js
//修改类组件里的showMessage 和 handleClick 函数。
showMessage = (name) => {
console.log('类组件: ' + name);
};
handleClick = () => {
let name = this.props.name;
//此处:
//1 、()=>this.showMessage(name)是闭包。父函数是handleClick,
//2、闭包会把当前的局部变量name持有,然后,调用 showMessage(name)时,把name的值传入showMessge里
setTimeout(()=>this.showMessage(name), 3000);
};
```
### **五、类组件和函数式组件区别:**
在react的模式和开发思维上,函数组件和类组件还是有区别的。
1、各自的特点:
1)类的组件主要是面向对象编程,是建立在继承之上,它的生命周期等核心概念的特点 2)函数组件主要是函数式编程,无副作用,并且在引用的时候透明的特点
2、使用场景:
因为两者主打的特点不一样,所以在使用场景上自然就存在一些差异了: 如果组件依赖生命周期,并且它的设计上需要继承的特性,我们在选择上更倾向类组件会更合适一点
由于HOOK的推出,逐渐降低了生命周期的概念,那么函数组件可以更好的取代类组件的开发,而且官方也推崇“组合优于继承”的思想,所以类组件的优势也在慢慢的淡出。更多关于web培训的问题,欢迎咨询千锋教育在线名师。千锋教育拥有多年IT培训服务经验,采用全程面授高品质、高体验培养模式,拥有国内一体化教学管理及学员服务,助力更多学员实现高薪梦想。