面试官:React 里类组件和函数组件的区别?

101 阅读6分钟

用户问题:React 里类组件和函数组件的区别?

解答:
在 React 中,组件可以分为类组件(Class Components)函数组件(Functional Components) 。虽然它们都可以用来构建用户界面,但在语法、生命周期、状态管理等方面存在一些重要的区别。以下是类组件和函数组件的主要区别及其适用场景。

1. 定义方式

类组件(Class Components)

  • 类组件是基于 ES6 的 class 语法定义的组件,继承自 React.Component
  • 类组件可以拥有自己的状态(state)和生命周期方法(如 componentDidMountcomponentWillUnmount 等)。
示例:
import React, { Component } from 'react';

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

  componentDidMount() {
    console.log('组件已挂载');
  }

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

  render() {
    return (
      <div>
        <p>计数器: {this.state.count}</p>
        <button onClick={this.increment}>点击增加</button>
      </div>
    );
  }
}

函数组件(Functional Components)

  • 函数组件是使用普通 JavaScript 函数定义的组件,通常更简洁。
  • 在 React 16.8 及之后版本中,函数组件可以通过 Hooks 来管理状态和副作用,而不需要依赖类组件的生命周期方法。
示例:
import React, { useState, useEffect } from 'react';

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

  useEffect(() => {
    console.log('组件已挂载或更新');
    return () => {
      console.log('组件即将卸载');
    };
  }, []);

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

  return (
    <div>
      <p>计数器: {count}</p>
      <button onClick={increment}>点击增加</button>
    </div>
  );
}

2. 状态管理

类组件

  • 类组件通过 this.state 和 this.setState() 来管理状态。
  • this.setState() 是异步的,React 会批量更新状态以提高性能。
示例:
this.setState({ count: this.state.count + 1 });

函数组件

  • 函数组件通过 useState() Hook 来管理状态。
  • useState() 返回一个数组,第一个元素是当前状态值,第二个元素是一个更新状态的函数。
示例:
const [count, setCount] = useState(0);
setCount(count + 1);

3. 生命周期方法

类组件

  • 类组件有明确的生命周期方法,如 componentDidMountcomponentDidUpdatecomponentWillUnmount 等。
  • 这些生命周期方法可以帮助你在组件的不同阶段执行特定的操作,比如数据获取、清理定时器等。
示例:
componentDidMount() {
  console.log('组件已挂载');
}

componentDidUpdate(prevProps, prevState) {
  if (prevState.count !== this.state.count) {
    console.log('计数器已更新:', this.state.count);
  }
}

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

函数组件

  • 函数组件通过 useEffect() Hook 来替代生命周期方法。
  • useEffect() 可以模拟 componentDidMountcomponentDidUpdate 和 componentWillUnmount 的行为。
示例:
useEffect(() => {
  console.log('组件已挂载或更新');

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

useEffect(() => {
  console.log('计数器已更新:', count);
}, [count]); // 依赖项数组中的 count 发生变化时触发

4. 事件处理

类组件

  • 在类组件中,事件处理函数需要绑定到组件实例上,通常在构造函数中使用 this 绑定,或者使用箭头函数来避免手动绑定。
示例:
constructor(props) {
  super(props);
  this.increment = this.increment.bind(this); // 手动绑定
}

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

函数组件

  • 函数组件中不需要手动绑定 this,因为函数组件没有实例化的过程。你可以直接定义事件处理函数,并且它们已经自动绑定了正确的上下文。
示例:
const increment = () => {
  setCount(count + 1);
};

5. 性能优化

类组件

  • 类组件的状态更新是通过 this.setState() 实现的,React 会批量更新状态以提高性能。
  • 如果你需要优化类组件的渲染性能,可以使用 shouldComponentUpdate 方法来控制组件是否需要重新渲染。
示例:
shouldComponentUpdate(nextProps, nextState) {
  return nextState.count !== this.state.count;
}

函数组件

  • 函数组件可以通过 React.memo 或 useMemouseCallback 等 Hooks 来优化性能。
  • React.memo 是一个高阶组件,用于防止不必要的重新渲染。
  • useMemo 和 useCallback 可以用于缓存计算结果和回调函数,减少不必要的计算。
示例:
const MemoizedComponent = React.memo(function MyComponent() {
  // 只有当 props 发生变化时才会重新渲染
});

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

const memoizedCallback = useCallback(
  () => {
9    doSomething(a, b);
10  },
11  [a, b],
12);

6. 代码简洁性

类组件

  • 类组件由于需要继承 React.Component,并且需要手动绑定 this,代码相对较为冗长。
  • 生命周期方法的引入使得类组件的代码结构更加复杂,尤其是在处理多个生命周期时。

函数组件

  • 函数组件更加简洁,代码量较少,尤其是结合 Hooks 使用时,逻辑更加清晰。
  • 函数组件没有 this 绑定的问题,减少了潜在的错误。

7. 社区趋势

类组件

  • 随着 React 16.8 引入 Hooks,类组件的使用逐渐减少。尽管类组件仍然可以使用,但社区越来越倾向于使用函数组件和 Hooks。

函数组件

  • 函数组件和 Hooks 已成为 React 社区的主流选择,官方文档也推荐优先使用函数组件。Hooks 提供了更好的可组合性和可读性,使得代码更加模块化和易于维护。

8. 总结

特性类组件(Class Components)函数组件(Functional Components)
定义方式使用 class 继承 React.Component使用普通函数定义
状态管理使用 this.state 和 this.setState()使用 useState() Hook
生命周期方法明确的生命周期方法(如 componentDidMount 等)使用 useEffect() Hook
事件处理需要手动绑定 this不需要绑定 this
性能优化使用 shouldComponentUpdate使用 React.memouseMemouseCallback
代码简洁性代码较为冗长,尤其是处理多个生命周期时代码简洁,逻辑清晰
社区趋势逐渐被函数组件取代成为主流选择,官方推荐

9. 何时选择类组件?

尽管函数组件和 Hooks 已经成为主流,但在某些情况下,你可能仍然需要使用类组件:

  • 旧项目升级:如果你正在维护一个使用类组件的旧项目,迁移到函数组件可能需要较大的改动,因此可以继续使用类组件。
  • 复杂的生命周期逻辑:如果你的组件有非常复杂的生命周期逻辑,且难以用 useEffect 表达,类组件可能会更直观。
  • 某些第三方库的限制:有些第三方库可能仍然依赖类组件的生命周期方法,或者不支持 Hooks。

10. 何时选择函数组件?

大多数情况下,你应该优先选择函数组件和 Hooks:

  • 现代 React 开发:函数组件和 Hooks 是现代 React 开发的最佳实践,能够让你的代码更加简洁、易读、易维护。
  • 状态管理和副作用处理useState 和 useEffect 等 Hooks 提供了强大的状态管理和副作用处理能力,适用于大多数场景。
  • 社区支持:函数组件和 Hooks 拥有广泛的社区支持和丰富的文档资源,学习曲线较低。

11. 进一步探讨

  • 你是否有实际项目中使用过类组件或函数组件?你觉得它们各自的优缺点是什么?
  • 你是否对 Hooks 有更深入的兴趣?例如如何使用 useReducer 处理复杂的状态管理,或者如何使用 useContext 实现全局状态管理?
  • 你是否想了解更多关于 React 性能优化的最佳实践?例如如何避免不必要的重新渲染,或者如何使用 useMemo 和 useCallback 提高性能?