函数组件与类组件

聊聊这两兄弟到底有什么区别

先说结论:在 Hooks 出来之前,函数组件就是个"打酱油"的,只能展示点静态内容。类组件才是"全能选手"。但自从 React 16.8 推出 Hooks 之后,函数组件原地起飞,直接逆袭成了主流。

这就是 React 组件开发方式的演进史。

1. 写法对比

类组件

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

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

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

特点:

  • 是个 ES6 Class,得 extends React.Component
  • 必须写个 render() 方法返回 JSX
  • this.state 存状态,this.setState() 改状态
  • 各种方法里都要带个 this,贼麻烦

函数组件

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

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

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

特点:

  • 就是一个普普通通的函数
  • useState 存状态,setCount 改状态
  • 没有 this,清爽多了

2. 能力对比表

能力类组件函数组件(现在)
状态管理this.state + this.setState()useState, useReducer
生命周期componentDidMount 一大堆一个 useEffect 全搞定
上下文MyContext.ConsumeruseContext
副作用分散在各个生命周期里放在同一个 useEffect
性能优化shouldComponentUpdateReact.memo, useMemo
RefReact.createRef()useRef
逻辑复用HOC、Render Props 那一套自定义 Hook,简单粗暴

3. 心智模型完全不同

这才是理解两者区别的核心。

类组件:面向对象那套

类组件就像创建一个对象实例。这个实例在组件的整个生命周期里都存在,状态就挂在实例上面。

class Counter extends React.Component {
  this.state = { count: 0 };  // 状态长在实例身上
}

痛点来了:

  1. this 绑定烦死人

    // 必须这么写,不然 this 就丢了
    <button onClick={this.increment.bind(this)}>
    // 或者在构造函数里写
    this.increment = this.increment.bind(this);
  2. 逻辑被拆得稀碎

    • 订阅逻辑:componentDidMount 里写订阅
    • 取消订阅:componentWillUnmount 里写取消
    • 本来是一套完整的逻辑,非得拆到两个生命周期里
  3. 组件大了没法看

    • 一个类组件可能有 300 行
    • 状态、逻辑、方法全挤在一起
    • 改起来胆战心惊,生怕改坏啥

函数组件:函数式编程

函数组件每次渲染都是一个独立的函数调用。这次调用完了,函数里的变量就全销毁了,下次渲染又是全新的调用。

function Counter() {
  const [count, setCount] = useState(0); // 每次渲染都是新调用
  return <div>{count}</div>;
}

那状态咋保持?靠 Hooks。Hooks 帮 React 在多次渲染之间"记住"了状态。

好处就很明显了:

  • 不用写 class,代码更短
  • 不用纠结 this 是谁
  • 逻辑可以按功能分开,而不是按生命周期分开
  • 自定义 Hook 是真的香,状态逻辑可以抽出来到处复用

4. 常见坑

函数组件的闭包陷阱

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

  useEffect(() => {
    const id = setInterval(() => {
      // 大坑!这里的 count 永远是 0
      setCount(count + 1);
    }, 1000);
    return () => clearInterval(id);
  }, []); // 空依赖,只跑一次

  return <div>{count}</div>;
}

解释一下:effect 只在第一次渲染时执行一次,那时候 count 是 0。定时器每次触发都用的那个 0,永远不会变。

两种解法:

// 写法1:用函数式更新
setCount((c) => c + 1);

// 写法2:把 count 放进依赖数组
useEffect(() => {
  const id = setInterval(() => {
    setCount(count + 1);
  }, 1000);
  return () => clearInterval(id);
}, [count]); // count 变了就重新建定时器

类组件的 this 问题

忘了绑定 this,点击按钮直接报错——这几乎是每个 React 开发者的必经之路。

5. 到底该用哪个?

毫无疑问,新项目就用函数组件。

React 官方现在全是往函数组件方向搞的,新特性也都优先给函数组件。类组件基本属于"维护模式"了。

什么时候还得用类组件?

  1. 维护老项目,里面一大把类组件
  2. 某些组件特别稳定,改了可能引入 bug,那就别动
  3. 极少数特殊情况:getSnapshotBeforeUpdatecomponentDidCatch 目前在函数组件里没有完全等价的 Hook

面试咋说?

记住这几点:

  1. 历史视角:Hooks 是分水岭,之前函数组件是残血的,之后才完整了

  2. 核心区别:类组件是"持续存在的实例",函数组件是"每次独立的调用"——这句话说清楚,心智模型就对了

  3. 能力对应:能说出类组件的特性对应哪些 Hook

  4. 优势在哪:代码简洁、逻辑复用不用(自定义 Hook)管 this

  5. 提到坑:闭包陷阱及其解法,说明真的写过

  6. 态度明确:新开发就用函数组件,类组件只用于维护旧代码