React 函数组件和类组件的区别

1. 定义方式

  • 函数组件:使用 ES6 的函数定义组件,接收 props 作为参数,返回一个 JSX 元素或 null。
javascript 复制代码
function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}
  • 类组件:使用 ES6 的类定义组件,继承自 React.Component,通过 render 方法返回 JSX。
javascript 复制代码
class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

2. 状态管理

  • 函数组件:在 React 16.8 之前,函数组件没有自己的状态,需要通过外部状态管理库(如 Redux)或父组件传值来管理状态。React 16.8 引入了 Hooks 后,函数组件可以通过 useState Hook 管理自己的状态。
javascript 复制代码
import React, { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);
  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}
  • 类组件:通过 this.state 来管理组件的内部状态,并使用 this.setState 方法来更新状态。
javascript 复制代码
class Counter extends React.Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };
  }

  render() {
    return (
      <div>
        <p>You clicked {this.state.count} times</p>
        <button onClick={() => this.setState({ count: this.state.count + 1 })}>
          Click me
        </button>
      </div>
    );
  }
}

3. 生命周期方法

  • 函数组件:没有生命周期方法,但在 React 16.8 引入 Hooks 后,可以通过 useEffect Hook 来实现类似生命周期的功能。
javascript 复制代码
import React, { useState, useEffect } from 'react';

function DataFetching() {
  const [data, setData] = useState([]);

  useEffect(() => {
    fetch('https://api.example.com/data')
      .then(response => response.json())
      .then(data => setData(data));
  }, []); // 空数组表示类似 componentDidMount 的效果

  return (
    <div>
      {data.map(item => (
        <div key={item.id}>{item.name}</div>
      ))}
    </div>
  );
}
  • 类组件:拥有多个生命周期方法,如 componentDidMount、componentDidUpdate、componentWillUnmount 等,这些方法在组件的挂载、更新和卸载过程中自动调用。
javascript 复制代码
class DataFetching extends React.Component {
  constructor(props) {
    super(props);
    this.state = { data: [] };
  }

  componentDidMount() {
    fetch('https://api.example.com/data')
      .then(response => response.json())
      .then(data => this.setState({ data }));
  }

  render() {
    return (
      <div>
        {this.state.data.map(item => (
          <div key={item.id}>{item.name}</div>
        ))}
      </div>
    );
  }
}
  • 类组件:拥有多个生命周期方法,如 componentDidMount、componentDidUpdate、componentWillUnmount 等,这些方法在组件的挂载、更新和卸载过程中自动调用。
javascript 复制代码
class DataFetching extends React.Component {
  constructor(props) {
    super(props);
    this.state = { data: [] };
  }

  componentDidMount() {
    fetch('https://api.example.com/data')
      .then(response => response.json())
      .then(data => this.setState({ data }));
  }

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

4. 组件的复用性

  • 函数组件:更易于复用逻辑,可以通过自定义 Hooks 来提取组件逻辑,实现逻辑的复用。
javascript 复制代码
function useCounter(initialCount) {
  const [count, setCount] = useState(initialCount);
  const increment = () => setCount(count + 1);
  return { count, increment };
}

function Counter() {
  const { count, increment } = useCounter(0);
  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={increment}>Click me</button>
    </div>
  );
}
  • 类组件:复用逻辑通常需要借助高阶组件(HOC)或继承的方式,相对来说更加复杂。

5. 性能优化

  • 函数组件:可以通过 React.memo 对组件进行性能优化,避免不必要的重渲染。
javascript 复制代码
const MyComponent = React.memo(function MyComponent(props) {
  /* 只有 props 发生变化时才会重新渲染 */
  return /* ... */;
});
  • 类组件:可以通过 shouldComponentUpdate 方法来控制组件是否更新,从而优化性能。
javascript 复制代码
class MyComponent extends React.Component {
  shouldComponentUpdate(nextProps, nextState) {
    return nextProps.value !== this.props.value || nextState.count !== this.state.count;
  }

  render() {
    /* ... */
  }
}

React.memo 和shouldComponentUpdate 详细讲解

React.memo

React.memo 是 React 提供的一个高阶组件,用于优化函数组件的性能。 当组件的 props 没有发生变化时,它可以阻止组件的不必要的重新渲染。

用法

javascript 复制代码
import React, { memo } from 'react';

const MyComponent = (props) => {
  console.log('MyComponent render', props.value);
  return <div>{props.value}</div>;
};

// 使用 React.memo 包裹组件
const MemoizedMyComponent = memo(MyComponent, (prevProps, nextProps) => {
  // 自定义比较逻辑
  return prevProps.value === nextProps.value; // 如果返回 true,则不重新渲染
});

// 在父组件中使用
function ParentComponent() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <button onClick={() => setCount(count + 1)}>
        Update Count
      </button>
      <MemoizedMyComponent value={count} />
    </div>
  );
}

参数说明

  • 第一个参数:要被优化的函数组件。
  • 第二个参数(可选):一个比较函数,用于比较当前 props 和上一个 props。如果返回 true,组件不会重新渲染;如果返回 false,组件会重新渲染。如果不提供此函数,React 会使用默认的浅比较(shallow comparison)。

默认行为

如果不提供自定义的比较函数,React 会使用浅比较来判断 props 是否发生变化。浅比较会检查对象的引用是否相同,而不是进行深度比较。

javascript 复制代码
const MemoizedMyComponent = memo(MyComponent);
shouldComponentUpdate

shouldComponentUpdate 是类组件中的一个生命周期方法,用于控制组件是否应该在状态或属性发生变化时重新渲染。 如果返回 false,组件不会重新渲染;如果返回 true,组件会重新渲染。

用法

javascript 复制代码
class MyComponent extends React.Component {
  shouldComponentUpdate(nextProps, nextState) {
    // 自定义比较逻辑
    return nextProps.value !== this.props.value || nextState.count !== this.state.count;
  }

  render() {
    console.log('MyComponent render', this.props.value, this.state.count);
    return <div>{this.props.value}</div>;
  }
}

// 在父组件中使用
function ParentComponent() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <button onClick={() => setCount(count + 1)}>
        Update Count
      </button>
      <MyComponent value={count} />
    </div>
  );
}
对比
特性 React.memo shouldComponentUpdate
适用组件类型 函数组件 类组件
使用方式 高阶组件,包裹函数组件 类组件的生命周期方法
参数 两个参数:要优化的组件和自定义比较函数(可选) 两个参数: 和
默认行为 浅比较 无默认行为,需要手动实现比较逻辑
性能影响 优化函数组件的渲染性能 优化类组件的渲染性能
适用场景 简单的展示型函数组件,或需要根据 props 变化控制渲染的场景 复杂的类组件,需要精细控制渲染逻辑的场景
代码简洁性 代码简洁,易于使用 需要手动实现比较逻辑,代码可能较为复杂
选择建议
  • 如果你使用的是函数组件,建议使用 React.memo 来优化性能。
  • 如果你使用的是类组件,建议使用 shouldComponentUpdate 来优化性能。
  • 在复杂的业务场景中,可以结合 React.memo 和 shouldComponentUpdate 来实现更精细的性能优化。

6. 代码简洁性

  • 函数组件:代码更为简洁,逻辑清晰,尤其是使用 Hooks 后,可以将相关逻辑放在一起,增强可读性。
  • 类组件:代码相对复杂,需要处理 this 的指向问题,且生命周期方法分散在不同地方,逻辑可能不够直观。

7. 使用场景

  • 函数组件:适用于简单的展示型组件或逻辑较为简单的场景,随着 Hooks 的引入,函数组件的功能得到了极大的增强,现在也被广泛用于复杂的业务场景。
  • 类组件:适用于需要管理复杂状态和生命周期的组件,或者在一些特殊的场景下(如需要使用 ref 来访问 DOM 节点)。

总结

特性 函数组件 类组件
定义方式 使用函数定义,接收 参数,返回 JSX 使用类定义,继承自 ,通过 方法返回 JSX
状态管理 React 16.8 之前无状态,16.8 之后通过 Hook 管理状态 通过 和 管理状态
生命周期方法 无生命周期方法,通过 Hook 实现类似功能 拥有多个生命周期方法,如 、 等
复用逻辑 通过自定义 Hooks 复用逻辑 通过高阶组件(HOC)或继承复用逻辑
性能优化 使用 使用
代码简洁性 更为简洁,逻辑清晰 代码相对复杂,需处理 指向
使用场景 简单展示型组件或逻辑简单的场景,现在也适用于复杂业务场景 复杂状态管理和生命周期控制的场景,或特殊场景(如需要访问 DOM 节点)
相关推荐
风骏时光牛马20 分钟前
数据可视化管理平台
前端
哎哟喂呢哈30 分钟前
VedioJS 10
javascript·html5
仿生狮子5 小时前
OpenAI 攻下千禧年难题之后,开发者似乎无动于衷
前端·数学·vibecoding
夏幻灵8 小时前
JavaScript this 面试:五种绑定规则、优先级与常见面试陷阱
开发语言·javascript·面试
Lvan的前端笔记8 小时前
docker:每个前端项目一个 Nginx 容器还是只有一个Nginx容器
前端·nginx·docker
三天不学习9 小时前
Egg.js 4 突然爆火,原因是否归结于AI 原生落地需求爆发?
前端·javascript·全栈·egg.js
xcs1940510 小时前
前端 vue 的前端页面debugger 进不去
前端·javascript·vue.js
明月_清风11 小时前
Deno 终局来了:从挑战 Node 到被 Cloudflare 收编
前端·后端·node.js
Csvn12 小时前
框架性能优化
前端
回眸&啤酒鸭13 小时前
【回眸】OpenSwarm 多智能体协作系统实战指南
大数据·前端·人工智能