React 类组件 vs 函数组件:从演进到实践,一篇彻底讲透

在这里插入图片描述


在 React 的发展历程中,组件的编写方式经历了巨大的演变。从最初的 类组件(Class Components) 一统天下,到后来 函数组件(Function Components) 凭借 Hooks 实现逆袭,成为当前官方推荐的主流。理解这两种组件的区别、优缺点以及适用场景,是每一位 React 开发者必备的核心知识。本文将深入剖析两者,带你彻底掌握它们。


一、 什么是类组件与函数组件?

在深入区别之前,我们先通过一个流程图快速建立宏观认知,了解如何根据场景在两者之间做出选择:

新项目/无复杂生命周期/需逻辑复用
维护旧项目/是Error Boundary
类组件特点
使用this.state/setState管理状态
使用生命周期方法
如componentDidMount
逻辑复用困难
HOC/Render Props
代码相对繁琐, this指向问题
函数组件特点
使用useState等Hooks管理状态
使用useEffect处理副作用
逻辑复用方便
自定义Hook
代码简洁, 易于测试
创建新组件
如何选择组件类型?
无悬念: 函数组件 + Hooks
不得不: 类组件

1. 类组件 (Class Components)

类组件是 ES6 的 class,它通过继承 React.Component 来创建,并使用 render 方法返回需要渲染的 JSX。

核心特征:

  • 必须继承 React.ComponentReact.PureComponent
  • 必须定义 render() 方法,该方法返回 JSX 元素。
  • 拥有内部状态 (this.state) 和生命周期方法 (如 componentDidMount)。

代码示例:

import React, { Component } from 'react';

// 定义一个类组件
class ClassComponentExample extends Component {
  // 1. 构造函数,初始化状态 (state)
  constructor(props) {
    super(props); // 必须调用 super(props)
    this.state = {
      count: 0,
      message: 'Hello, Class Component!'
    };
    // 2. 为了解决 this 指向问题,需要手动绑定方法
    this.handleClick = this.handleClick.bind(this);
  }

  // 3. 自定义方法:更新状态
  handleClick() {
    this.setState({
      count: this.state.count + 1
    });
  }

  // 4. 生命周期方法:组件挂载后执行
  componentDidMount() {
    console.log('Component did mount!');
  }

  // 5. 生命周期方法:组件更新后执行
  componentDidUpdate() {
    console.log('Component did update! Count is:', this.state.count);
  }

  // 6. 必须定义的 render 方法
  render() {
    return (
      <div>
        <h1>{this.state.message}</h1>
        <p>You clicked {this.state.count} times</p>
        {/* 调用方法,注意 this 的绑定 */}
        <button onClick={this.handleClick}>Click me</button>
        {/* 也可以使用箭头函数避免绑定,但每次都会创建新函数 */}
        {/* <button onClick={() => this.handleClick()}>Click me</button> */}
      </div>
    );
  }
}

export default ClassComponentExample;

2. 函数组件 (Function Components)

函数组件本质上就是一个 JavaScript 函数,它接收 props 作为参数,并直接返回需要渲染的 JSX。

核心特征 (Hooks 之前):

  • 只是一个普通的 JavaScript 函数。
  • 没有内部状态 (state)。
  • 没有生命周期方法。
  • 被称为“无状态组件”或“展示组件”,只负责接收 props 和渲染 UI。

核心特征 (Hooks 之后):

  • 通过 Hooks (如 useState, useEffect) 拥有了管理状态和副作用的能力。
  • 功能上已经完全等同于类组件,且代码更简洁。

代码示例 (使用 Hooks):

import React, { useState, useEffect } from 'react';

// 定义一个函数组件(使用 Hooks)
function FunctionComponentExample(props) {
  // 1. 使用 useState Hook 定义状态
  const [count, setCount] = useState(0);
  const [message, setMessage] = useState('Hello, Function Component!');

  // 2. 自定义函数:更新状态(无需担心 this 绑定)
  const handleClick = () => {
    setCount(prevCount => prevCount + 1); // 使用函数式更新
  };

  // 3. 使用 useEffect Hook 模拟生命周期
  // 相当于 componentDidMount + componentDidUpdate
  useEffect(() => {
    console.log('Component did mount or update! Count is:', count);
  }, [count]); // 依赖数组:只有当 count 变化时,effect 才会重新执行

  // 4. 模拟 componentDidMount(依赖数组为空)
  useEffect(() => {
    console.log('This only runs once, like componentDidMount');
  }, []);

  // 5. 直接返回 JSX,无需 render 方法
  return (
    <div>
      <h1>{message}</h1>
      <p>You clicked {count} times</p>
      <button onClick={handleClick}>Click me</button>
    </div>
  );
}

export default FunctionComponentExample;

二、 核心区别对比

特性类组件 (Class Components)函数组件 (Function Components with Hooks)
定义方式ES6 Class,继承 React.ComponentJavaScript 函数(普通或箭头)
状态管理this.statethis.setState()useState, useReducer Hook
生命周期生命周期方法 (componentDidMount, componentDidUpdate 等)useEffect Hook 模拟所有生命周期
this 指向需要手动绑定或使用类字段语法,容易出错没有 this,彻底避免指向问题
代码复杂度代码相对冗长,尤其是绑定和生命周期方法代码更简洁,逻辑更聚合(相关代码在一起)
逻辑复用高阶组件 (HOC)、Render Props自定义 Hooks,逻辑复用更简单自然
学习曲线需要理解 ES6 Class、this、生命周期概念需要理解 Hooks 的规则(如调用顺序)和闭包
性能使用 PureComponentshouldComponentUpdate 优化使用 React.memo, useMemo, useCallback 优化
未来趋势遗留代码中常见,React 团队不推荐在新项目中使用当前和未来的主流,React 官方推荐

三、 深入理解关键差异

1. 状态管理:心智模型的转变

  • 类组件: 状态是一个单一的对象 (this.state),更新时通过 this.setState() 合并更新。
    this.setState({ count: this.state.count + 1 }); // 合并更新
    
  • 函数组件: 状态被分解为多个独立的 state 变量,通过各自的 setter 函数更新。
    const [count, setCount] = useState(0);
    const [name, setName] = useState('John');
    setCount(c => c + 1); // 函数式更新,避免闭包陷阱
    

2. 生命周期 vs 副作用 Effect

这是两者最大的概念差异。

  • 类组件: 基于生命周期方法。将不同的逻辑分散到不同的方法中。

    • componentDidMount:发起网络请求、订阅事件。
    • componentDidUpdate:根据 props 或 state 变化执行操作。
    • componentWillUnmount:清除订阅、取消请求。
    • 问题:不相关的代码放在一起(如设置事件监听和请求数据),而相关的代码却被拆分(如订阅和取消订阅)。
  • 函数组件: 基于副作用 Effect。思维模式是“同步到某个状态”。

    • useEffect 允许你根据 依赖项 来执行和清理副作用。
    • 它将原本分散在不同生命周期中的相关代码聚合在了一起
    useEffect(() => {
      // 1. 建立订阅(相当于 didMount 和 didUpdate 中与 count 相关的部分)
      const subscription = props.source.subscribe();
      // 2. 返回一个清理函数(相当于 willUnmount 和 didUpdate 中下次 effect 执行前)
      return () => {
        subscription.unsubscribe();
      };
    }, [props.source]); // 3. 依赖项:只有当 props.source 改变时才会重新执行
    

    这种“关注点分离”的方式使得代码更容易理解和维护。

3. 逻辑复用:从模式到 Hook

  • 类组件: 使用高阶组件 (HOC)Render Props。这些模式虽然强大,但会导致“嵌套地狱”(Wrapper Hell),降低组件可读性。

    // HOC 示例
    const EnhancedComponent = withHOC(MyComponent);
    // Render Props 示例
    <DataProvider render={data => <Child data={data} />} />
    
  • 函数组件: 使用自定义 Hook。它允许你提取组件逻辑成为可重用的函数,且不会增加组件树的嵌套层级。

    // 自定义 Hook: useFriendStatus
    function useFriendStatus(friendID) {
      const [isOnline, setIsOnline] = useState(null);
      useEffect(() => {
        // 订阅好友状态逻辑...
      }, [friendID]);
      return isOnline;
    }
    
    // 在多个组件中使用它
    function FriendStatus(props) {
      const isOnline = useFriendStatus(props.friend.id); // 像调用函数一样使用逻辑
      return <div>{isOnline ? 'Online' : 'Offline'}</div>;
    }
    function FriendListItem(props) {
      const isOnline = useFriendStatus(props.friend.id); // 逻辑复用!
      return <li style={{ color: isOnline ? 'green' : 'black' }}>{props.friend.name}</li>;
    }
    

四、 总结与最佳实践

  1. 对于新项目和新组件:
    毫不犹豫地选择函数组件 + Hooks。 这是 React 现在和未来的发展方向,代码更简洁,逻辑更清晰,复用性更强。

  2. 对于现有项目:

    • 如果是旧的类组件,没有必要立刻重写为函数组件,除非你在进行重大重构或需要引入新的 Hooks 逻辑。
    • 在新的开发中,逐步采用函数组件。
  3. 类组件的遗留用例:

    • getSnapshotBeforeUpdatecomponentDidCatch 生命周期方法目前还没有完全对等的 Hooks 实现。因此,错误边界(Error Boundaries)目前必须用类组件来实现
    class ErrorBoundary extends React.Component {
      constructor(props) {
        super(props);
        this.state = { hasError: false };
      }
      static getDerivedStateFromError(error) {
        return { hasError: true };
      }
      componentDidCatch(error, errorInfo) {
        logErrorToMyService(error, errorInfo);
      }
      render() {
        if (this.state.hasError) {
          return <h1>Something went wrong.</h1>;
        }
        return this.props.children;
      }
    }
    

结论:
函数组件与 Hooks 的结合代表了 React 编程模式的一次重大飞跃。它解决了类组件在长期实践中暴露出的诸多问题,如复杂的 this 绑定、难以理解的生命周期和逻辑复用困难等。虽然学习 Hooks 需要一些新的心智模型,但其带来的好处是巨大的。拥抱函数组件,意味着你正在拥抱 React 的未来。

在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

北辰alk

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值