全国旗舰校区

不同学习城市 同样授课品质

北京

深圳

上海

广州

郑州

大连

武汉

成都

西安

杭州

青岛

重庆

长沙

哈尔滨

南京

太原

沈阳

合肥

贵阳

济南

下一个校区
就在你家门口
+
当前位置:首页  >  技术干货

为什么react选择了函数式组件(剖析原理)

发布时间:2022-06-06 11:54:00
发布人:wjy

以下代码,没有使用模块化的方式,使用的是CDN的方式。如果需要源代码,请从这个地址下载:

```text
链接:https://pan.baidu.com/s/1s57mr5AE_ecWBFZ5TJTwqw  提取码:f7x2
```

另外,这篇文章,主要是剖析**组件的初次渲染和重新渲染**。所以,其它部分不要太较劲。

为什么react选择了函数式组件

### **一、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();
```

为什么react选择了函数式组件1

### **2)、运行:**

**2.1)类组件:**

点击类组件的“调用带着定时器的函数” 按钮后,再点击”修改“按钮(**注意先后顺序**)。我们发现了:界面上打印的和控制台打印的是**一样的**。这是**不对的**:因为,我点击“调用带着定时器的函数” 按钮时,name的值是”张三疯“,应该打印”张三疯“

为什么react选择了函数式组件2

**2.2)函数式组件:**

点击类组件的“调用带着定时器的函数” 按钮后,再点击”修改“按钮(**注意先后顺序**)。我们发现了:界面上打印的和控制台里打印的**不一样**,这是**对的**:因为,我点击“调用带着定时器的函数” 按钮时,name的值是”张三疯“,应该打印”张三疯“

为什么react选择了函数式组件3

### **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选择了函数式组件4

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培训服务经验,采用全程面授高品质、高体验培养模式,拥有国内一体化教学管理及学员服务,助力更多学员实现高薪梦想。

相关文章

企业抖音小店保证金是多少

企业抖音小店保证金是多少

2023-10-08
抖音怎么弄抖音小店店铺

抖音怎么弄抖音小店店铺

2023-10-08
抖音小店怎么设置新人券领不到

抖音小店怎么设置新人券领不到

2023-10-08
开通抖音小店的保证金在哪里退

开通抖音小店的保证金在哪里退

2023-10-08

最新文章

上海物联网培训一般费用多少

上海物联网培训一般费用多少

2023-09-12
北京物联网培训费用大概多少

北京物联网培训费用大概多少

2023-09-12
北京物联网培训需要费用高不高

北京物联网培训需要费用高不高

2023-09-12
上海效果好的物联网培训费用高吗

上海效果好的物联网培训费用高吗

2023-09-12
在线咨询 免费试学 教程领取