返回首页

说说对 React 中类组件和函数组件的理解?有什么区别?

问题解析(面试官考察点)

面试官通过此问题主要考察:

  • 对 React 两种组件写法的掌握程度
  • 对 Hooks 的理解和应用
  • 对 this 绑定问题的理解
  • 能否根据场景选择合适的组件写法

核心概念(基础知识点)

类组件(Class Component)

通过 ES6 类的形式编写,必须继承 React.Component

  • 可以定义自己的 state
  • 可以使用生命周期方法
  • 通过 this 访问 props 和 state

函数组件(Function Component)

通过函数的形式编写,是 React 中定义组件最简单的方式:

  • 早期是无状态组件
  • Hooks 出现后可以使用 state 和生命周期
  • 没有 this,通过参数访问 props

详细解答(代码示例)

类组件示例

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

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

  render() {
    return (
      <div>
        <h1>Hello, {this.props.name}</h1>
        <p>Count: {this.state.count}</p>
        <button onClick={this.handleClick}>+1</button>
      </div>
    );
  }
}

函数组件示例

function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

// 使用 Hooks 的函数组件
import React, { useState, useEffect } from 'react';

function Welcome({ name }) {
  const [count, setCount] = useState(0);

  useEffect(() => {
    console.log('Component did mount or update');
    return () => {
      console.log('Component will unmount');
    };
  }, []);

  return (
    <div>
      <h1>Hello, {name}</h1>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>+1</button>
    </div>
  );
}

深入理解(原理剖析)

主要区别对比

特性 类组件 函数组件
编写形式 ES6 类 函数
状态管理 this.state / this.setState useState Hook
生命周期 完整的生命周期方法 useEffect 等 Hooks
this 绑定 需要处理 this 绑定问题 没有 this
代码量 相对较多 简洁
性能 需要实例化 无实例化开销
逻辑复用 HOC / render props 自定义 Hooks

状态管理对比

类组件:

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

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

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

函数组件:

function Counter() {
  const [count, setCount] = useState(0);

  const increment = () => {
    setCount(prevCount => prevCount + 1);
  };

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

生命周期对比

类组件生命周期:

class Example extends React.Component {
  componentDidMount() {
    console.log('组件挂载');
  }

  componentDidUpdate(prevProps, prevState) {
    console.log('组件更新');
  }

  componentWillUnmount() {
    console.log('组件卸载');
  }

  render() {
    return <div>Example</div>;
  }
}

函数组件 useEffect:

function Example() {
  useEffect(() => {
    console.log('组件挂载或更新');

    return () => {
      console.log('组件卸载或依赖变化');
    };
  }, []); // 空数组表示只在挂载和卸载时执行

  return <div>Example</div>;
}

调用方式的区别

函数组件调用:

// 你的代码
function SayHi() {
  return <p>Hello, React</p>;
}

// React 内部
const result = SayHi(props); // » <p>Hello, React</p>

类组件调用:

// 你的代码
class SayHi extends React.Component {
  render() {
    return <p>Hello, React</p>;
  }
}

// React 内部
const instance = new SayHi(props); // » SayHi {}
const result = instance.render();   // » <p>Hello, React</p>

获取渲染值的重要区别

函数组件(闭包特性):

function ProfilePage(props) {
  const showMessage = () => {
    alert('Followed ' + props.user);
  };

  const handleClick = () => {
    setTimeout(showMessage, 3000);
  };

  return <button onClick={handleClick}>Follow</button>;
}

类组件(this 可变):

class ProfilePage extends React.Component {
  showMessage() {
    alert('Followed ' + this.props.user);
  }

  handleClick() {
    setTimeout(() => this.showMessage(), 3000);
  }

  render() {
    return <button onClick={() => this.handleClick()}>Follow</button>;
  }
}

关键区别:

  • 函数组件:props 是函数参数,不会变化(闭包捕获)
  • 类组件:this.props 是可变的,可能获取到最新的值而非点击时的值

最佳实践

优先使用函数组件

  1. 代码更简洁

    • 没有 class 和 this
    • 逻辑更清晰
  2. 更容易测试

    • 纯函数更容易测试
    • 没有复杂的实例化过程
  3. 逻辑复用更方便

    • 自定义 Hooks
    • 比 HOC 和 render props 更直观

自定义 Hooks 示例

// 自定义 Hook
function useWindowWidth() {
  const [width, setWidth] = useState(window.innerWidth);

  useEffect(() => {
    const handleResize = () => setWidth(window.innerWidth);
    window.addEventListener('resize', handleResize);
    return () => window.removeEventListener('resize', handleResize);
  }, []);

  return width;
}

// 使用自定义 Hook
function MyComponent() {
  const width = useWindowWidth();
  return <div>Window width: {width}</div>;
}

何时使用类组件

虽然函数组件是推荐做法,但以下情况可能需要类组件:

  • 维护旧代码
  • 需要使用 getSnapshotBeforeUpdate 等特定生命周期
  • 错误边界(Error Boundaries)
class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError(error) {
    return { hasError: true };
  }

  componentDidCatch(error, errorInfo) {
    console.error('Error:', error, errorInfo);
  }

  render() {
    if (this.state.hasError) {
      return <h1>Something went wrong.</h1>;
    }
    return this.props.children;
  }
}

面试要点

  1. 主要区别总结

    • 语法:类 vs 函数
    • 状态:this.state vs useState
    • 生命周期:生命周期方法 vs useEffect
    • this:需要绑定 vs 没有 this
  2. Hooks 的优势

    • 代码复用更简单(自定义 Hooks)
    • 逻辑聚合(相关逻辑放在一起)
    • 没有 this 绑定问题
  3. 闭包 vs this

    • 函数组件使用闭包捕获 props/state
    • 类组件的 this 总是指向最新实例
    • 异步操作中可能产生不同行为
  4. 性能考虑

    • 函数组件没有实例化开销
    • React.memo 可以实现类似 PureComponent 的效果
    • useMemo/useCallback 可以优化性能
  5. 常见面试题

    • 函数组件和类组件的区别?
    • 为什么推荐使用函数组件?
    • Hooks 能否完全替代类组件?
    • 函数组件中的闭包陷阱是什么?
  6. 现代 React 趋势

    • React 团队推荐使用函数组件
    • 新特性优先支持 Hooks
    • 类组件不会被移除,但不再新增特性