大白话React第二章深入理解阶段

大白话React第二章深入理解阶段

1. 学习组件生命周期(类组件)

咱们前面已经知道组件就像蛋糕店里的小蛋糕模块,而组件生命周期就好比是一个小蛋糕从制作到卖出去的整个过程。

  • 挂载阶段 :这就相当于蛋糕开始制作,把各种原料准备好放到模具里。在 React 里,组件创建并插入到 DOM 中的过程就是挂载阶段。比如 constructor 就像是你准备原料的过程,在这里初始化一些数据;render 就是把原料变成蛋糕的样子,返回要显示的内容;componentDidMount 就好比蛋糕做好了,从烤箱里拿出来放到展示柜上了,这时候可以做一些后续操作,比如去加载一些外部数据。
jsx 复制代码
import React, { Component } from 'react';

class CakeComponent extends Component {
  // 准备原料,初始化数据
  constructor(props) {
    super(props);
    console.log('开始准备蛋糕原料');
    this.state = {
      flavor: '巧克力'
    };
  }

  // 把原料变成蛋糕的样子
  render() {
    console.log('正在制作蛋糕,确定外观');
    return (
      <div>
        <p>这是一个 {this.state.flavor} 口味的蛋糕</p>
      </div>
    );
  }

  // 蛋糕做好放到展示柜了,可以做后续操作
  componentDidMount() {
    console.log('蛋糕做好啦,放到展示柜咯,可以去宣传一下');
  }
}

export default CakeComponent;
  • 更新阶段 :这就像是蛋糕在展示柜里,根据顾客的反馈或者新的流行趋势,对蛋糕进行一些修改。当组件的 props 或者 state 发生变化时,就会进入更新阶段。componentDidUpdate 就好比修改完蛋糕后,重新调整一下展示的位置,或者再装饰一下。
jsx 复制代码
import React, { Component } from 'react';

class CakeComponent extends Component {
  constructor(props) {
    super(props);
    this.state = {
      flavor: '巧克力'
    };
  }

  changeFlavor = () => {
    this.setState({ flavor: '草莓' });
  };

  render() {
    return (
      <div>
        <p>这是一个 {this.state.flavor} 口味的蛋糕</p>
        <button onClick={this.changeFlavor}>换成草莓口味</button>
      </div>
    );
  }

  componentDidUpdate(prevProps, prevState) {
    if (prevState.flavor!== this.state.flavor) {
      console.log('蛋糕口味换啦,重新调整一下展示方式');
    }
  }
}

export default CakeComponent;
  • 卸载阶段 :这就好比蛋糕卖出去了,展示柜里就不需要它了。组件从 DOM 中移除时就是卸载阶段,componentWillUnmount 就像是把蛋糕从展示柜里拿走,清理一下展示柜的位置。
jsx 复制代码
import React, { Component } from 'react';

class CakeComponent extends Component {
  constructor(props) {
    super(props);
    this.state = {
      flavor: '巧克力'
    };
  }

  render() {
    return (
      <div>
        <p>这是一个 {this.state.flavor} 口味的蛋糕</p>
      </div>
    );
  }

  componentWillUnmount() {
    console.log('蛋糕卖出去啦,清理一下展示柜位置');
  }
}

export default CakeComponent;
2. 掌握 props 和 state
  • propsprops 就像是蛋糕店的顾客订单要求。当顾客来买蛋糕时,会告诉店员要什么口味、多大尺寸,这些信息就通过 props 传递给制作蛋糕的师傅(组件)。
jsx 复制代码
// 定义一个蛋糕组件,接收顾客订单要求
import React from 'react';

const Cake = (props) => {
  return (
    <div>
      <p>这是一个 {props.flavor} 口味,{props.size} 寸的蛋糕</p>
    </div>
  );
};

export default Cake;

// 在另一个组件里使用蛋糕组件,传递订单要求
import React from 'react';
import Cake from './Cake';

const CakeShop = () => {
  return (
    <div>
      <Cake flavor="香草" size="8" />
    </div>
  );
};

export default CakeShop;
  • statestate 就像是蛋糕师傅自己记录的一些信息,比如蛋糕的剩余数量、今天的销售目标等。这些信息是组件自己管理和维护的,并且可以随时改变。
jsx 复制代码
import React, { Component } from 'react';

class CakeShop extends Component {
  constructor(props) {
    super(props);
    // 初始化蛋糕剩余数量
    this.state = {
      cakeCount: 10
    };
  }

  sellCake = () => {
    if (this.state.cakeCount > 0) {
      // 卖出一个蛋糕,更新剩余数量
      this.setState({ cakeCount: this.state.cakeCount - 1 });
    }
  };

  render() {
    return (
      <div>
        <p>当前蛋糕剩余数量: {this.state.cakeCount}</p>
        <button onClick={this.sellCake}>卖一个蛋糕</button>
      </div>
    );
  }
}

export default CakeShop;
3. 学习事件处理

事件处理就像是蛋糕店处理顾客的各种请求。顾客可能会点击菜单、下单、询问价格等,我们要对这些操作做出响应。

jsx 复制代码
import React, { Component } from 'react';

class CakeShop extends Component {
  constructor(props) {
    super(props);
    this.state = {
      selectedCake: null
    };
  }

  // 处理顾客选择蛋糕的事件
  selectCake = (cakeName) => {
    this.setState({ selectedCake: cakeName });
  };

  render() {
    return (
      <div>
        <h1>蛋糕店菜单</h1>
        <ul>
          <li onClick={() => this.selectCake('巧克力蛋糕')}>巧克力蛋糕</li>
          <li onClick={() => this.selectCake('草莓蛋糕')}>草莓蛋糕</li>
        </ul>
        {this.state.selectedCake && (
          <p>你选择了 {this.state.selectedCake}</p>
        )}
      </div>
    );
  }
}

export default CakeShop;
4. 处理表单数据

处理表单数据就像是蛋糕店让顾客填写订单信息,然后把这些信息保存下来。

jsx 复制代码
import React, { Component } from 'react';

class CakeOrderForm extends Component {
  constructor(props) {
    super(props);
    this.state = {
      flavor: '',
      size: ''
    };
  }

  // 处理输入框内容变化
  handleChange = (e) => {
    const { name, value } = e.target;
    this.setState({ [name]: value });
  };

  // 处理表单提交
  handleSubmit = (e) => {
    e.preventDefault();
    console.log('你下单了一个', this.state.size, '寸的', this.state.flavor, '口味蛋糕');
  };

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          选择口味:
          <input
            type="text"
            name="flavor"
            value={this.state.flavor}
            onChange={this.handleChange}
          />
        </label>
        <br />
        <label>
          选择尺寸:
          <input
            type="text"
            name="size"
            value={this.state.size}
            onChange={this.handleChange}
          />
        </label>
        <br />
        <button type="submit">下单</button>
      </form>
    );
  }
}

export default CakeOrderForm;

通过这些内容的学习,你就能更深入地理解 React 组件的工作原理,以及如何处理数据和用户交互啦。

代码示例中的this指向什么?

在 React 的代码示例里,this 这玩意儿有点像一个神奇的"指针",它指向的是当前正在执行操作的那个"东西",在类组件里,主要指向的就是这个类组件的实例,下面我用大白话结合前面的代码示例详细给你说说。

1. 在构造函数 constructor 里的 this

咱们先看之前 CakeComponent 组件的构造函数:

jsx 复制代码
import React, { Component } from 'react';

class CakeComponent extends Component {
  constructor(props) {
    super(props);
    console.log('开始准备蛋糕原料');
    this.state = {
      flavor: '巧克力'
    };
  }
  // 其他代码...
}

在这个构造函数里,this 就像是一个"主人牌",它指的就是当前这个 CakeComponent 组件的实例。就好比你开了一家蛋糕店,每个蛋糕就是一个组件实例,this 就是贴在这个蛋糕上的标签,告诉别人这是哪个蛋糕。当你写 this.state = { flavor: '巧克力' }; 的时候,就是在说给这个特定的蛋糕贴上"巧克力口味"的标签,把这个口味信息存到这个蛋糕(组件实例)自己的小本本(state)里。

2. 在自定义方法里的 this

再看看 CakeComponent 里的 changeFlavor 方法:

jsx 复制代码
class CakeComponent extends Component {
  constructor(props) {
    super(props);
    this.state = {
      flavor: '巧克力'
    };
  }

  changeFlavor = () => {
    this.setState({ flavor: '草莓' });
  };

  // 其他代码...
}

这里的 this 同样指向 CakeComponent 组件的实例。changeFlavor 方法就像是一个给蛋糕换口味的小工具,当你用这个工具去操作蛋糕的时候,this 能让你明确知道是在操作哪个蛋糕。this.setState 就是在说,我要给贴了这个"主人牌"(this)的蛋糕把口味改成草莓味。

不过要注意哦,如果不用箭头函数,而是用普通函数的写法,this 的指向就可能会出问题。比如这样:

jsx 复制代码
class CakeComponent extends Component {
  constructor(props) {
    super(props);
    this.state = {
      flavor: '巧克力'
    };
  }

  changeFlavor() {
    this.setState({ flavor: '草莓' });
  }

  render() {
    return (
      <div>
        <p>这是一个 {this.state.flavor} 口味的蛋糕</p>
        <button onClick={this.changeFlavor}>换成草莓口味</button>
      </div>
    );
  }
}

在普通函数里,this 的指向会根据函数的调用方式改变,当你点击按钮调用 changeFlavor 时,this 可能就不是指向组件实例了,这样 this.setState 就会出错。而箭头函数不会自己创建 this,它会继承外层作用域的 this,所以用箭头函数就能保证 this 一直指向组件实例。

3. 在生命周期方法里的 this

componentDidMountcomponentDidUpdate 这些生命周期方法里的 this,也是指向组件实例。比如:

jsx 复制代码
class CakeComponent extends Component {
  constructor(props) {
    super(props);
    this.state = {
      flavor: '巧克力'
    };
  }

  componentDidMount() {
    console.log('蛋糕做好啦,这个蛋糕的口味是', this.state.flavor);
  }

  // 其他代码...
}

componentDidMount 里,this 还是那个"主人牌",通过 this.state.flavor 就能知道这个蛋糕的口味信息,就像看贴在蛋糕上的标签一样。

总的来说,在 React 的类组件里,this 就像是一个标记,让你能准确地找到当前正在操作的那个组件实例,方便你对这个实例的各种属性(比如 state)和方法进行操作。

如何在类组件的方法中正确使用 this?

在 React 的类组件方法里正确使用 this,就好比在一场游戏里准确找到自己的角色,然后用这个角色去完成各种任务。下面我结合之前蛋糕店的例子,用大白话详细跟你说说怎么在类组件方法里正确使用 this

1. 构造函数里绑定 this

咱先看看之前做蛋糕的代码例子:

jsx 复制代码
import React, { Component } from 'react';

class CakeShop extends Component {
  constructor(props) {
    super(props);
    this.state = {
      cakeCount: 10
    };
    // 手动绑定 this
    this.sellCake = this.sellCake.bind(this);
  }

  sellCake() {
    if (this.state.cakeCount > 0) {
      this.setState({ cakeCount: this.state.cakeCount - 1 });
    }
  }

  render() {
    return (
      <div>
        <p>当前蛋糕剩余数量: {this.state.cakeCount}</p>
        <button onClick={this.sellCake}>卖一个蛋糕</button>
      </div>
    );
  }
}

export default CakeShop;

在构造函数 constructor 里,this 指向的是当前这个 CakeShop 组件实例,就好像给这个蛋糕店贴上了一个独一无二的标签。当我们定义了 sellCake 这个方法,它一开始并不知道自己要操作哪个蛋糕店(也就是不知道 this 指向谁)。所以我们用 this.sellCake = this.sellCake.bind(this); 这行代码,就相当于给 sellCake 方法也贴上了和蛋糕店一样的标签,让它知道自己要操作的就是这个特定的蛋糕店。这样在 sellCake 方法里使用 this.state.cakeCount 时,就能准确地访问到这个蛋糕店的蛋糕剩余数量。

2. 使用箭头函数

箭头函数是个很方便的工具,能帮我们避免 this 指向混乱的问题。看下面这个例子:

jsx 复制代码
import React, { Component } from 'react';

class CakeShop extends Component {
  constructor(props) {
    super(props);
    this.state = {
      cakeCount: 10
    };
  }

  // 使用箭头函数定义方法
  sellCake = () => {
    if (this.state.cakeCount > 0) {
      this.setState({ cakeCount: this.state.cakeCount - 1 });
    }
  };

  render() {
    return (
      <div>
        <p>当前蛋糕剩余数量: {this.state.cakeCount}</p>
        <button onClick={this.sellCake}>卖一个蛋糕</button>
      </div>
    );
  }
}

export default CakeShop;

箭头函数不会自己创建一个新的 this,它会直接继承外层作用域的 this。在这个例子里,sellCake 箭头函数继承的就是 CakeShop 组件实例的 this,就好像它天生就带着和蛋糕店一样的标签,所以在函数里使用 this.state.cakeCount 时,能直接找到这个蛋糕店的蛋糕剩余数量,不需要我们手动去绑定 this

3. 内联箭头函数

有时候我们也会在 JSX 里直接用内联箭头函数来处理事件,比如:

jsx 复制代码
import React, { Component } from 'react';

class CakeShop extends Component {
  constructor(props) {
    super(props);
    this.state = {
      cakeCount: 10
    };
  }

  sellCake() {
    if (this.state.cakeCount > 0) {
      this.setState({ cakeCount: this.state.cakeCount - 1 });
    }
  }

  render() {
    return (
      <div>
        <p>当前蛋糕剩余数量: {this.state.cakeCount}</p>
        <button onClick={() => this.sellCake()}>卖一个蛋糕</button>
      </div>
    );
  }
}

export default CakeShop;

在这个例子里,我们在 onClick 事件里用了内联箭头函数 () => this.sellCake()。这里的 this 同样是继承自 CakeShop 组件实例。当点击按钮时,箭头函数会调用 sellCake 方法,并且能正确地把 this 传递过去,让 sellCake 方法知道要操作的是哪个蛋糕店。不过要注意,内联箭头函数每次渲染都会创建一个新的函数,可能会对性能有一点影响,所以如果是简单的事件处理,推荐用前面两种方法。

总结一下,要在类组件方法里正确使用 this,可以在构造函数里手动绑定 this,或者使用箭头函数让它自动继承外层的 this,这样就能准确地操作组件实例的各种属性和方法啦。

如何解决在类组件中 this 指向不正确的问题?

在 React 的类组件里,this 指向不正确就好比你在一个大商场里迷路了,找不到自己要去的店铺。不过别担心,有几种办法能帮你解决这个问题,下面我结合之前蛋糕店的例子,用大白话给你详细讲讲。

1. 在构造函数中手动绑定 this

咱们先看之前的 CakeShop 组件代码:

jsx 复制代码
import React, { Component } from 'react';

class CakeShop extends Component {
  constructor(props) {
    super(props);
    this.state = {
      cakeCount: 10
    };
    // 手动绑定 this
    this.sellCake = this.sellCake.bind(this);
  }

  sellCake() {
    if (this.state.cakeCount > 0) {
      this.setState({ cakeCount: this.state.cakeCount - 1 });
    }
  }

  render() {
    return (
      <div>
        <p>当前蛋糕剩余数量: {this.state.cakeCount}</p>
        <button onClick={this.sellCake}>卖一个蛋糕</button>
      </div>
    );
  }
}

export default CakeShop;

在普通函数(像 sellCake 方法)里,this 的指向会根据函数的调用方式而改变。当我们在 constructor 里写 this.sellCake = this.sellCake.bind(this); 时,就好比给 sellCake 方法发了一张"身份证",上面写着它属于当前这个 CakeShop 组件实例。这样一来,不管 sellCake 方法在哪里被调用,它都知道自己要操作的是哪个蛋糕店,this 就能正确地指向 CakeShop 组件实例,从而能准确地访问 this.state.cakeCount 这个蛋糕剩余数量。

2. 使用箭头函数定义方法

看下面修改后的代码:

jsx 复制代码
import React, { Component } from 'react';

class CakeShop extends Component {
  constructor(props) {
    super(props);
    this.state = {
      cakeCount: 10
    };
  }

  // 使用箭头函数定义方法
  sellCake = () => {
    if (this.state.cakeCount > 0) {
      this.setState({ cakeCount: this.state.cakeCount - 1 });
    }
  };

  render() {
    return (
      <div>
        <p>当前蛋糕剩余数量: {this.state.cakeCount}</p>
        <button onClick={this.sellCake}>卖一个蛋糕</button>
      </div>
    );
  }
}

export default CakeShop;

箭头函数就像是一个忠诚的小跟班,它不会自己创建一个新的 this,而是直接继承它外层作用域的 this。在这个例子里,sellCake 箭头函数继承的就是 CakeShop 组件实例的 this。这就好比小跟班一直紧紧跟着主人(组件实例),不管走到哪里,都知道自己的主人是谁。所以在 sellCake 函数里使用 this.state.cakeCount 时,能直接找到这个蛋糕店的蛋糕剩余数量,不会出现 this 指向混乱的问题。

3. 使用内联箭头函数调用方法

再看另一种情况:

jsx 复制代码
import React, { Component } from 'react';

class CakeShop extends Component {
  constructor(props) {
    super(props);
    this.state = {
      cakeCount: 10
    };
  }

  sellCake() {
    if (this.state.cakeCount > 0) {
      this.setState({ cakeCount: this.state.cakeCount - 1 });
    }
  }

  render() {
    return (
      <div>
        <p>当前蛋糕剩余数量: {this.state.cakeCount}</p>
        <button onClick={() => this.sellCake()}>卖一个蛋糕</button>
      </div>
    );
  }
}

export default CakeShop;

这里我们在 onClick 事件里用了内联箭头函数 () => this.sellCake()。这个内联箭头函数同样会继承它外层作用域(也就是 CakeShop 组件实例)的 this。当你点击按钮时,箭头函数会去调用 sellCake 方法,并且能把正确的 this 传递给 sellCake 方法。不过要注意,每次组件渲染的时候,内联箭头函数都会重新创建一个新的函数,这可能会对性能有那么一点点影响,所以如果只是简单的事件处理,前面两种方法会更好一些。

总之,要解决类组件中 this 指向不正确的问题,你可以在构造函数里手动绑定 this,或者使用箭头函数来让它自动继承正确的 this 指向。这样,你在操作组件实例的属性和方法时就不会迷路啦。

除了类组件,函数组件中的 this 指向什么?

在 React 里,除了类组件,还有函数组件。和类组件不太一样,函数组件里可没有 this 这个东西指向特定对象,下面我用大白话给你详细说说为啥是这样。

1. 函数组件就像一个独立小工匠

你可以把函数组件想象成一个独立的小工匠。这个小工匠接到一个制作任务(也就是接收一些数据作为参数),然后专心做出一个成品(返回一段 JSX 代码来展示界面)。它不需要像类组件那样有一个"自己的家"(实例),所以也就不需要 this 来指向自己。

比如下面这个简单的函数组件:

jsx 复制代码
import React from 'react';

// 定义一个名为 Greeting 的函数组件
const Greeting = (props) => {
    return <h1>Hello, {props.name}!</h1>;
};

export default Greeting;

这里的 Greeting 函数组件就像一个小工匠,它接收 props 这个参数,里面包含了要显示的名字信息。然后它直接根据这个信息返回一段显示问候语的 JSX 代码。在整个过程中,它不需要用 this 来表示自己,因为它只是一个单纯的函数,每次调用它,它就按照给定的参数干活,没有自己的状态或者属性需要用 this 去访问。

2. 没有实例就不需要 this 标记

类组件就像一个有自己房子(实例)的大家庭,this 就像是这个大家庭的门牌号,通过这个门牌号可以找到家里的各种东西(属性和方法)。但是函数组件没有这样的房子(实例),它就是一个临时的工作坊,来一个任务就做一个任务,做完就拉倒。所以根本不需要 this 来标记自己。

再看一个稍微复杂点的例子:

jsx 复制代码
import React from 'react';

const Product = (props) => {
    const showDetails = () => {
        // 这里不需要 this,直接用 props 里的数据
        console.log(`Product name: ${props.name}, Price: ${props.price}`);
    };

    return (
        <div>
            <h2>{props.name}</h2>
            <p>Price: {props.price}</p>
            <button onClick={showDetails}>Show details</button>
        </div>
    );
};

export default Product;

Product 函数组件里,有一个 showDetails 函数用来显示产品的详细信息。在这个函数里,我们直接使用 props 里的数据,而不需要用 this 去访问。因为函数组件没有自己的实例,所有的数据都是通过参数传递进来的,所以不需要 this 来帮忙找东西。

3. 用 Hooks 管理状态也不用 this

后来 React 出了 Hooks,让函数组件也能有自己的状态。但是即使使用了 Hooks,还是不需要 this。比如用 useState Hook 来管理状态:

jsx 复制代码
import React, { useState } from 'react';

const Counter = () => {
    // 使用 useState Hook 定义一个状态变量 count 和更新函数 setCount
    const [count, setCount] = useState(0);

    const increment = () => {
        // 直接调用 setCount 来更新状态,不需要 this
        setCount(count + 1);
    };

    return (
        <div>
            <p>Count: {count}</p>
            <button onClick={increment}>Increment</button>
        </div>
    );
};

export default Counter;

Counter 函数组件里,我们用 useState 定义了一个状态变量 count 和更新函数 setCount。在 increment 函数里,我们直接调用 setCount 来更新状态,不需要像类组件那样用 this.setState,因为函数组件没有 this 这个概念。

总之,函数组件就像一个个独立干活的小工匠,它们没有自己的实例,不需要用 this 来指向自己,所有的数据和操作都是通过参数和函数来完成的。

类组件和函数组件有什么区别?

在 React 里,类组件和函数组件就像是两种不同类型的厨师,他们都能做出美食(构建页面),但做菜的方式和特点不太一样。下面我用大白话详细给你说说它们的区别。

1. 定义方式不同

  • 类组件 :类组件就像是一个专业的厨师团队,有一套完整的流程和规则。定义类组件的时候,你得创建一个类,这个类要继承自 React.Component,就好比厨师团队要遵循一套特定的行业规范。在这个类里,你要写很多方法,像 render 方法就相当于厨师团队做菜的核心步骤,规定了要做出什么样的菜品(返回什么样的页面结构)。
jsx 复制代码
import React, { Component } from 'react';

// 定义一个类组件,继承自 React.Component
class CakeShop extends Component {
    render() {
        return <h1>欢迎来到蛋糕店!</h1>;
    }
}

export default CakeShop;
  • 函数组件 :函数组件就像是一个独立的厨师,比较随性自由。定义函数组件很简单,就是写一个普通的 JavaScript 函数,这个函数接收一些参数(通常叫 props),然后返回一段 JSX 代码,就像独立厨师根据客人的要求(参数)做出一道菜(页面结构)。
jsx 复制代码
import React from 'react';

// 定义一个函数组件
const CakeShop = (props) => {
    return <h1>欢迎来到 {props.name} 蛋糕店!</h1>;
};

export default CakeShop;

2. 状态管理不同

  • 类组件 :类组件就像是一个有仓库的厨师团队,他们可以在仓库(state)里存放各种食材(数据),并且可以随时去仓库拿食材来做菜,也能往仓库里添加或更换食材。在类组件里,你可以在构造函数里初始化 state,然后用 this.setState 方法来更新 state
jsx 复制代码
import React, { Component } from 'react';

class CakeShop extends Component {
    constructor(props) {
        super(props);
        // 初始化仓库(state)里的食材(数据)
        this.state = {
            cakeCount: 10
        };
    }

    sellCake = () => {
        // 从仓库拿食材(更新 state)
        this.setState({ cakeCount: this.state.cakeCount - 1 });
    };

    render() {
        return (
            <div>
                <p>当前蛋糕剩余数量: {this.state.cakeCount}</p>
                <button onClick={this.sellCake}>卖一个蛋糕</button>
            </div>
        );
    }
}

export default CakeShop;
  • 函数组件 :在以前,函数组件就像是一个没有仓库的独立厨师,只能根据客人现有的要求(props)做菜,没办法自己存放和管理食材(数据)。不过后来 React 出了 Hooks,有了 useState 这个工具,函数组件也能有自己的小抽屉(状态)来存放一些简单的食材(数据)了。
jsx 复制代码
import React, { useState } from 'react';

const CakeShop = () => {
    // 创建一个小抽屉(状态),初始放 10 个蛋糕
    const [cakeCount, setCakeCount] = useState(10);

    const sellCake = () => {
        // 从抽屉拿蛋糕(更新状态)
        setCakeCount(cakeCount - 1);
    };

    return (
        <div>
            <p>当前蛋糕剩余数量: {cakeCount}</p>
            <button onClick={sellCake}>卖一个蛋糕</button>
        </div>
    );
};

export default CakeShop;

3. 生命周期方法不同

  • 类组件 :类组件就像是一个按部就班工作的厨师团队,有一套完整的工作流程,从开店准备(挂载阶段),到中间不断调整菜品(更新阶段),再到关店收拾(卸载阶段),每个阶段都有对应的方法可以去执行一些特定的任务。比如 componentDidMount 就像是开店准备好后,开始正式营业,可以去做一些宣传活动(加载数据等)。
jsx 复制代码
import React, { Component } from 'react';

class CakeShop extends Component {
    componentDidMount() {
        console.log('蛋糕店开业啦,开始宣传!');
    }

    render() {
        return <h1>欢迎来到蛋糕店!</h1>;
    }
}

export default CakeShop;
  • 函数组件 :函数组件就像是一个比较随性的独立厨师,没有那么多固定的工作流程。不过有了 Hooks 之后,useEffect 可以模拟一些生命周期方法的功能。比如在 useEffect 里可以做一些类似 componentDidMount 的事情。
jsx 复制代码
import React, { useEffect } from 'react';

const CakeShop = () => {
    useEffect(() => {
        console.log('蛋糕店开业啦,开始宣传!');
        // 这里可以返回一个清理函数,类似 componentWillUnmount
        return () => {
            console.log('蛋糕店关门啦,收拾东西!');
        };
    }, []);

    return <h1>欢迎来到蛋糕店!</h1>;
};

export default CakeShop;

4. this 的使用不同

  • 类组件 :类组件里的 this 就像是厨师团队的队长,通过 this 可以指挥团队里的各种资源(属性和方法)。在类组件里,你可以用 this 来访问 state、调用方法等。不过要注意 this 的指向问题,有时候需要手动绑定。
  • 函数组件 :函数组件里没有 this 这个概念,因为它就像一个独立厨师,不需要队长来指挥。所有的数据和操作都是通过参数和函数来完成的。

5. 性能和复杂度不同

  • 类组件:类组件相对复杂一些,因为有很多方法和生命周期要管理,就像厨师团队的管理比较麻烦。而且每次状态更新,可能会触发整个组件的重新渲染,性能上可能会有一些损耗。
  • 函数组件:函数组件比较简单,代码量通常也比较少,就像独立厨师干活比较简洁。而且 React 对函数组件做了很多优化,性能上可能会更好一些。

总的来说,类组件功能强大,适合处理复杂的逻辑和状态管理;函数组件简洁灵活,适合做一些简单的展示性组件。在实际开发中,可以根据具体的需求来选择使用哪种组件。

如何优化组件的生命周期方法?

在 React 里,组件的生命周期方法就像是一场表演的不同环节,优化这些环节能让表演更精彩,也就是让组件性能更好、运行更流畅。下面咱就用大白话详细说说怎么优化组件的生命周期方法。

1. 挂载阶段优化(componentDidMount

componentDidMount 就像是表演开场前的准备工作,这时候要确保只做必要的事情,别做多余的活儿。

  • 避免重复请求数据 :假如你要在组件挂载的时候从服务器获取数据,就像开场前要从后台拿道具,只拿一次就行,别来回跑。比如说你做一个新闻列表组件,在 componentDidMount 里发一次请求获取新闻数据,别在这方法里写循环重复发请求,不然服务器会被你折腾坏,页面加载也会变慢。
jsx 复制代码
import React, { Component } from 'react';

class NewsList extends Component {
    constructor(props) {
        super(props);
        this.state = {
            news: []
        };
    }

    componentDidMount() {
        // 只发一次请求获取新闻数据
        fetch('https://api.example.com/news')
          .then(response => response.json())
          .then(data => this.setState({ news: data }));
    }

    render() {
        return (
            <div>
                {this.state.news.map(item => (
                    <p key={item.id}>{item.title}</p>
                ))}
            </div>
        );
    }
}

export default NewsList;
  • 初始化第三方库:如果要在组件挂载时初始化一些第三方库,像图表库、地图库啥的,确保这些库的初始化代码简洁高效。就好比开场前布置舞台道具,要快速又准确地把道具摆好。

2. 更新阶段优化(shouldComponentUpdatecomponentDidUpdate

更新阶段就像是表演过程中根据观众反馈调整表演内容,要聪明地判断要不要调整,别瞎折腾。

  • 使用 shouldComponentUpdate 避免不必要的渲染shouldComponentUpdate 就像是一个小门卫,能决定要不要让组件重新渲染。在这个方法里,你可以对比 propsstate 的新旧值,如果没变化就不让组件重新渲染,省点力气。
jsx 复制代码
import React, { Component } from 'react';

class Counter extends Component {
    constructor(props) {
        super(props);
        this.state = {
            count: 0
        };
    }

    shouldComponentUpdate(nextProps, nextState) {
        // 如果 count 没变化,就不重新渲染
        if (this.state.count === nextState.count) {
            return false;
        }
        return true;
    }

    increment = () => {
        this.setState({ count: this.state.count + 1 });
    };

    render() {
        return (
            <div>
                <p>Count: {this.state.count}</p>
                <button onClick={this.increment}>Increment</button>
            </div>
        );
    }
}

export default Counter;
  • componentDidUpdate 里避免无限循环componentDidUpdate 就像是表演过程中的小调整,但别调整过头了,导致一直调整个没完。比如说你在这个方法里更新 state,要确保有条件限制,别每次更新都触发 componentDidUpdate 然后又更新 state,形成无限循环。
jsx 复制代码
import React, { Component } from 'react';

class Example extends Component {
    constructor(props) {
        super(props);
        this.state = {
            value: 0
        };
    }

    componentDidUpdate(prevProps, prevState) {
        // 只有当 value 小于 10 时才更新 state
        if (prevState.value < 10) {
            this.setState({ value: prevState.value + 1 });
        }
    }

    render() {
        return <p>Value: {this.state.value}</p>;
    }
}

export default Example;

3. 卸载阶段优化(componentWillUnmount

componentWillUnmount 就像是表演结束后收拾舞台,要把该清理的东西都清理干净,别留下垃圾。

  • 清除定时器和事件监听器 :如果你在组件里用了定时器,像 setInterval 或者 setTimeout,在组件卸载的时候要清除它们,不然它们会一直在后台运行,浪费资源。还有事件监听器,比如给窗口添加的滚动监听,组件卸载时也要移除,不然可能会导致一些奇怪的问题。
jsx 复制代码
import React, { Component } from 'react';

class TimerComponent extends Component {
    constructor(props) {
        super(props);
        this.state = {
            seconds: 0
        };
        this.timer = null;
    }

    componentDidMount() {
        this.timer = setInterval(() => {
            this.setState({ seconds: this.state.seconds + 1 });
        }, 1000);
    }

    componentWillUnmount() {
        // 清除定时器
        clearInterval(this.timer);
    }

    render() {
        return <p>Seconds passed: {this.state.seconds}</p>;
    }
}

export default TimerComponent;

通过这些优化方法,能让组件的生命周期方法更高效,让你的 React 应用跑得又快又稳,就像一场精彩又流畅的表演一样。

相关推荐
视觉CG14 分钟前
【Viewer.js】vue3封装图片查看器
开发语言·javascript·vue.js
java1234_小锋26 分钟前
一周学会Flask3 Python Web开发-redirect重定向
前端·python·flask·flask3
琑9544 分钟前
nextjs项目搭建——头部导航
开发语言·前端·javascript
light多学一点1 小时前
视频的分片上传
前端
Gazer_S1 小时前
【Windows系统node_modules删除失败(EPERM)问题解析与应对方案】
前端·javascript·windows
bigyoung2 小时前
基于 React 的列表实现方案,包含创建和编辑状态,使用 Modal 弹框和表单的最佳实践
前端
沙子迷了蜗牛眼2 小时前
antv G6绘制流程图
开发语言·javascript·ecmascript
乌木前端2 小时前
包管理工具lock文件的作用
前端·javascript
司玄2 小时前
3dtiles平移旋转原理及可视化工具实现
前端
m0_748236582 小时前
本地部署轻量级web开发框架Flask并实现无公网ip远程访问开发界面
前端·tcp/ip·flask