创见博客
React的类组件与函数组件
七崽爱吃小饼干2026/01/05阅读 2专栏 React

一、 核心定义与本质区别

这是两者最基础的差异,决定了后续所有特性的不同:

特性类组件(Class Component)函数组件(Functional Component)
本质类型基于 ES6 Class 语法,继承 React.Component/React.PureComponent基于 JavaScript 普通函数/箭头函数,无继承关系
组件实例有自身实例(this 指向组件实例对象)无组件实例(不存在 this 指向,无需处理 this 绑定问题)
核心定位(历史演变)早期 React 主流组件,提供完整状态和生命周期能力早期为「无状态组件」(仅接收 props 渲染视图),React 16.8 Hooks 推出后成为主流,具备完整能力
最简结构必须包含 render() 方法返回 JSX直接返回 JSX(无需 render 关键字)

最简示例对比

jsx
// 类组件
class ClassGreeting extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>; // 依赖 this 访问 props
  }
}

// 函数组件
const FunctionGreeting = (props) => {
  return <h1>Hello, {props.name}</h1>; // 直接访问 props 参数,无 this
};

二、 状态管理能力差异

状态管理是组件的核心能力之一,两者的实现方式、语法特性差异显著:

特性类组件函数组件(Hooks)
状态管理 API基于 this.state(状态对象)和 this.setState()(状态更新方法)基于 useState(简单状态)/ useReducer(复杂状态)Hook
状态初始化两种方式:1. 构造函数 constructor 中初始化;2. 类体直接赋值(ES7 语法)直接在组件内部调用 useState,支持惰性初始化(传入函数避免复杂计算重复执行)
状态更新特性setState 是异步的,自动合并对象类型状态(仅合并顶层属性)useState 的更新函数是异步的,替换式更新(对象/数组状态需手动合并原有值)
依赖当前状态更新可直接使用 this.state(需注意异步陷阱),也支持函数式更新推荐使用函数式更新(传入 prevState 参数),避免异步更新导致的状态误差

状态管理示例对比

jsx
// 类组件:状态管理
class ClassCounter extends React.Component {
  // 状态初始化
  state = { count: 0, name: "计数器" };

  // 状态更新(自动合并顶层属性)
  increment = () => {
    this.setState({ count: this.state.count + 1 }); // 普通更新
    // 函数式更新(依赖当前状态,更安全)
    // this.setState(prevState => ({ count: prevState.count + 1 }));
  };

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

// 函数组件:状态管理
import { useState } from 'react';

const FunctionCounter = () => {
  // 状态初始化(支持惰性初始化:useState(() => 0))
  const [count, setCount] = useState(0);
  const [name, setName] = useState("计数器");

  const increment = () => {
    setCount(count + 1); // 普通更新
    // 函数式更新(推荐,依赖当前状态时更准确)
    // setCount(prevCount => prevCount + 1);
  };

  // 对象类型状态:需手动合并(替换式更新)
  const [user, setUser] = useState({ name: "张三", age: 18 });
  const updateAge = () => {
    setUser(prevUser => ({ ...prevUser, age: prevUser.age + 1 }));
  };

  return (
    <div>
      <p>{name}:{count}</p>
      <button onClick={increment}>+1</button>
    </div>
  );
};

三、 生命周期/副作用处理差异

类组件有明确的生命周期方法,函数组件通过 useEffect Hook 统一处理副作用,实现生命周期的功能:

特性类组件函数组件(Hooks)
处理方式提供 固定的生命周期方法(分阶段执行),职责明确无专门生命周期方法,通过 useEffect Hook 统一处理所有副作用(网络请求、定时器、DOM 操作等)
核心生命周期映射1. 挂载:componentDidMount;2. 更新:componentDidUpdate;3. 卸载:componentWillUnmountuseEffect 通过「依赖数组」实现映射:1. 挂载(空数组 []);2. 更新(指定依赖);3. 卸载(返回清理函数)
逻辑聚合性相关逻辑可能分散在多个生命周期方法中(如网络请求在 componentDidMount,更新请求在 componentDidUpdate)相关逻辑聚合在一个 useEffect 中,代码内聚性更强,更易维护

生命周期/副作用示例对比

jsx
// 类组件:生命周期处理
class ClassLifecycle extends React.Component {
  state = { data: [] };

  // 挂载阶段:发起网络请求、初始化定时器
  componentDidMount() {
    this.fetchData();
    this.timer = setInterval(() => console.log("定时器执行"), 1000);
  }

  // 更新阶段:props 变化时重新请求数据
  componentDidUpdate(prevProps) {
    if (prevProps.id !== this.props.id) {
      this.fetchData();
    }
  }

  // 卸载阶段:清理副作用(清除定时器、取消请求)
  componentWillUnmount() {
    clearInterval(this.timer);
  }

  fetchData = () => {
    fetch(`https://api.example.com/data/${this.props.id}`)
      .then(res => res.json())
      .then(data => this.setState({ data }));
  };

  render() {
    return <div>{this.state.data.map(item => item.name)}</div>;
  }
}

// 函数组件:useEffect 模拟生命周期
import { useState, useEffect } from 'react';

const FunctionLifecycle = (props) => {
  const [data, setData] = useState([]);

  // 聚合:挂载 + 更新 + 卸载逻辑
  useEffect(() => {
    // 模拟 componentDidMount + componentDidUpdate
    const fetchData = async () => {
      const res = await fetch(`https://api.example.com/data/${props.id}`);
      const result = await res.json();
      setData(result);
    };
    fetchData();

    // 模拟 componentWillUnmount(清理函数)
    const timer = setInterval(() => console.log("定时器执行"), 1000);
    return () => {
      clearInterval(timer);
    };
  }, [props.id]); // 依赖 props.id,仅当 id 变化时执行

  return <div>{data.map(item => item.name)}</div>;
};

四、 事件处理差异

两者的事件处理核心差异在于 this 指向问题,这也是类组件的常见痛点:

特性类组件函数组件(Hooks)
this 指向问题普通类方法的 this 默认指向 undefined(严格模式下),存在「this 丢失」问题无 this 概念,事件处理函数无需绑定 this,直接使用即可
this 绑定解决方案三种方式:1. 箭头函数(类实例属性);2. 构造函数中 bind;3. 渲染时内联箭头函数无需绑定,直接定义箭头函数或普通函数即可
代码简洁性需额外处理 this 绑定,代码冗余无绑定开销,代码更简洁直观

事件处理示例对比

jsx
// 类组件:事件处理(需处理 this 绑定)
class ClassEvent extends React.Component {
  // 方案1:箭头函数(推荐,无需手动绑定)
  handleClick = () => {
    console.log("点击事件", this.props.name); // this 指向组件实例
  };

  // 方案2:构造函数 bind 绑定
  // constructor(props) {
  //   super(props);
  //   this.handleClick = this.handleClick.bind(this);
  // }
  // handleClick() {
  //   console.log("点击事件", this.props.name);
  // }

  render() {
    return <button onClick={this.handleClick}>类组件按钮</button>;
  }
}

// 函数组件:事件处理(无需 this 绑定)
const FunctionEvent = (props) => {
  // 直接定义函数,无需处理 this
  const handleClick = () => {
    console.log("点击事件", props.name);
  };

  return <button onClick={handleClick}>函数组件按钮</button>;
};

五、 性能优化差异

两者的性能优化思路一致(避免不必要的重渲染、减少重复计算),但使用的 API 不同:

特性类组件函数组件(Hooks)
组件级缓存基于 React.PureComponent(自动浅比较 props/state)或 shouldComponentUpdate(手动控制更新)基于 React.memo(高阶组件,浅比较 props,对应 PureComponent)
计算结果缓存无原生 API,需手动缓存(如将计算结果存入 state)基于 useMemo Hook,缓存复杂计算结果,避免每次渲染重复计算
函数引用缓存无原生 API,需手动处理(如将函数绑定为实例属性)基于 useCallback Hook,缓存函数引用,避免因函数引用变化导致子组件不必要渲染
优化核心逻辑控制组件是否触发更新(通过 shouldComponentUpdate/PureComponent)1. 组件级:React.memo;2. 计算级:useMemo;3. 函数级:useCallback

性能优化示例对比

jsx
// 类组件:性能优化
class ClassOptimize extends React.PureComponent {
  // 若不使用 PureComponent,可手动编写 shouldComponentUpdate
  // shouldComponentUpdate(nextProps, nextState) {
  //   return nextProps.name !== this.props.name; // 仅 name 变化时更新
  // }

  // 复杂计算:需手动缓存,否则每次 render 重复执行
  getExpensiveValue = () => {
    return this.props.list.filter(item => item > 0).sort((a, b) => a - b);
  };

  render() {
    const sortedList = this.getExpensiveValue();
    return <div>{sortedList.join(',')}</div>;
  }
}

// 函数组件:性能优化
import { useState, memo, useMemo, useCallback } from 'react';

// 组件级缓存:React.memo
const FunctionOptimize = memo((props) => {
  // 计算结果缓存:useMemo,仅 list 变化时重新计算
  const sortedList = useMemo(() => {
    return props.list.filter(item => item > 0).sort((a, b) => a - b);
  }, [props.list]);

  // 函数引用缓存:useCallback,仅依赖变化时更新函数引用
  const handleClick = useCallback(() => {
    console.log("点击", props.name);
  }, [props.name]);

  return (
    <div>
      <div>{sortedList.join(',')}</div>
      <button onClick={handleClick}>按钮</button>
    </div>
  );
});

六、 逻辑复用差异

逻辑复用是组件设计的重要需求,两者的实现方式差异极大,这也是函数组件成为主流的核心原因之一:

特性类组件函数组件(Hooks)
复用方式1. 高阶组件(HOC);2. Render Props(渲染属性)基于 自定义 Hooks(独立函数,复用逻辑)
复用痛点1. 高阶组件:产生「嵌套地狱」(组件层级嵌套过深,难以调试);2. Render Props:代码嵌套复杂,可读性差无嵌套问题,自定义 Hooks 是扁平的函数调用,代码简洁、可读性强
逻辑内聚性复用逻辑与组件自身逻辑分离,难以维护复用逻辑聚合在自定义 Hooks 中,与组件逻辑无缝集成
学习成本较高(需理解高阶组件、Render Props 的设计思想)较低(自定义 Hooks 是普通函数,符合直觉)

逻辑复用示例对比

jsx
// 类组件:高阶组件实现逻辑复用(定时器逻辑)
function withTimer(WrappedComponent) {
  return class extends React.Component {
    state = { time: 0 };
    timer = null;

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

    componentWillUnmount() {
      clearInterval(this.timer);
    }

    render() {
      return <WrappedComponent {...this.props} time={this.state.time} />;
    }
  };
}

// 使用高阶组件
class ClassTimer extends React.Component {
  render() {
    return <div>计时:{this.props.time} 秒</div>;
  }
}
const ClassTimerWithHOC = withTimer(ClassTimer);

// 函数组件:自定义 Hooks 实现逻辑复用(定时器逻辑)
import { useState, useEffect } from 'react';

// 自定义 Hook:复用定时器逻辑
function useTimer(initialTime = 0) {
  const [time, setTime] = useState(initialTime);

  useEffect(() => {
    const timer = setInterval(() => {
      setTime(prev => prev + 1);
    }, 1000);
    return () => clearInterval(timer);
  }, []);

  return time;
}

// 使用自定义 Hook
const FunctionTimer = () => {
  const time = useTimer(0); // 直接调用 Hook,复用逻辑
  return <div>计时:{time} 秒</div>;
};

七、 核心差异总结表

对比维度类组件函数组件(Hooks)
定义形式ES6 Class,继承 React.Component普通函数/箭头函数,无继承
组件实例有实例,存在 this 指向无实例,无 this 概念
状态管理this.state + setState(对象合并更新)useState/useReducer(替换式更新)
生命周期/副作用固定生命周期方法(分散逻辑)useEffect(聚合逻辑,依赖数组控制执行)
事件处理需处理 this 绑定问题无需 this 绑定,代码简洁
性能优化PureComponent/shouldComponentUpdateReact.memo/useMemo/useCallback
逻辑复用高阶组件/Render Props(嵌套问题)自定义 Hooks(扁平复用,易维护)
代码简洁性模板化冗余,代码量较大轻量简洁,逻辑内聚性强
学习成本较高(this + 生命周期 + 复用模式)较低(Hooks 语义清晰,符合直觉)
未来趋势逐渐被淘汰,仅用于老项目维护React 官方推荐,主流方案,支持最新特性

八、 适用场景总结

  1. 类组件适用场景:

    • 维护老旧 React 项目(项目中大量使用类组件,无需重构为函数组件)
    • 团队成员对 Hooks 不熟悉,暂时无法迁移
    • 特殊场景(如需要使用 getSnapshotBeforeUpdate 等小众生命周期方法,函数组件实现复杂)
  2. 函数组件适用场景:

    • 新项目开发(React 官方推荐,后续会持续优化 Hooks 生态)
    • 需复用组件逻辑(自定义 Hooks 比高阶组件更优雅)
    • 追求代码简洁性和可维护性(无 this 困扰,逻辑聚合)
    • 需使用 React 最新特性(如 Server Components、Concurrent Mode 等,优先支持函数组件)

总结

  1. 类组件和函数组件的核心差异源于定义形式和是否存在组件实例,这决定了两者在状态管理、事件处理等方面的语法差异。
  2. 函数组件通过 Hooks 实现了类组件的所有能力,且在代码简洁性、逻辑复用、性能优化灵活性上更具优势,是当前 React 开发的主流。
  3. 类组件并未完全过时,但仅适用于老项目维护,新项目应优先使用函数组件。
  4. 两者的核心功能一致(渲染视图、管理状态、处理副作用),差异主要在于实现方式和开发体验。
评论
0/100