返回首页

React 构建组件的方式有哪些?区别?

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

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

  • 对 React 组件发展历史的了解
  • 对不同组件写法的掌握
  • 能否根据场景选择合适的组件类型
  • 对现代 React 最佳实践的理解

核心概念(基础知识点)

组件化的优势

组件化开发是现代前端开发的核心理念:

  1. 降低耦合度:保持接口不变的情况下,可以替换不同组件
  2. 调试方便:通过排除法快速定位问题
  3. 提高可维护性:每个组件职责单一,逻辑简单
  4. 代码复用:组件可在多个场景重复使用

三种构建方式

  1. 函数式创建:简单、无状态(Hooks 之前)
  2. React.createClass:早期推荐方式(已废弃)
  3. 继承 React.Component:类组件方式

详细解答(代码示例)

方式一:函数式创建(推荐)

// 基础函数组件
function HelloComponent(props) {
  return <div>Hello {props.name}</div>;
}

// 使用箭头函数
const HelloComponent = (props) => {
  return <div>Hello {props.name}</div>;
};

// 解构 props
const HelloComponent = ({ name, age }) => {
  return <div>Hello {name}, you are {age}</div>;
};

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

function Timer() {
  const [seconds, setSeconds] = useState(0);

  useEffect(() => {
    const interval = setInterval(() => {
      setSeconds(s => s + 1);
    }, 1000);

    return () => clearInterval(interval);
  }, []);

  return <div>Seconds: {seconds}</div>;
}

方式二:React.createClass(已废弃)

// 这种写法已经不再推荐使用
var HelloComponent = React.createClass({
  getInitialState: function() {
    return { count: 0 };
  },

  handleClick: function() {
    this.setState({ count: this.state.count + 1 });
  },

  render: function() {
    return (
      <div onClick={this.handleClick}>
        Count: {this.state.count}
      </div>
    );
  }
});

注意: React 15.5 开始已废弃,React 16 彻底移除。

方式三:继承 React.Component

import React from 'react';

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

  tick() {
    this.setState(state => ({
      seconds: state.seconds + 1
    }));
  }

  componentDidMount() {
    this.interval = setInterval(() => this.tick(), 1000);
  }

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

  render() {
    return (
      <div>
        Seconds: {this.state.seconds}
      </div>
    );
  }
}

深入理解(原理剖析)

三种方式对比

特性 函数组件 React.createClass 类组件
语法 函数 对象配置 ES6 类
状态 Hooks getInitialState constructor
this 绑定 无 this 自动绑定 需要手动绑定
生命周期 useEffect 完整生命周期 完整生命周期
性能 一般 需要实例化
当前状态 推荐 已废弃 可用

函数组件的演变

React Hooks 之前(无状态):

function Hello({ name }) {
  return <h1>Hello, {name}</h1>;
}
// 只能接收 props,无法管理状态

React Hooks 之后(有状态):

import { useState, useEffect } from 'react';

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

  useEffect(() => {
    document.title = `Count: ${count}`;
  }, [count]);

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

编译后的代码对比

JSX 代码:

function HelloComponent(props) {
  return <div>Hello {props.name}</div>;
}

编译后(Babel):

function HelloComponent(props) {
  return React.createElement(
    "div",
    null,
    "Hello ",
    props.name
  );
}

类组件的内部实现

// React 内部处理类组件的伪代码
function mountClassComponent(element) {
  const { type, props } = element;

  // 实例化组件
  const instance = new type(props);

  // 调用生命周期
  if (instance.componentWillMount) {
    instance.componentWillMount();
  }

  // 调用 render
  const renderedElement = instance.render();

  // 继续渲染子树
  return mount(renderedElement);
}

最佳实践

优先使用函数组件

// 推荐:函数组件 + Hooks
import React, { useState, useCallback, useMemo } from 'react';

function UserList({ users }) {
  const [filter, setFilter] = useState('');

  const filteredUsers = useMemo(() => {
    return users.filter(user =>
      user.name.toLowerCase().includes(filter.toLowerCase())
    );
  }, [users, filter]);

  const handleFilterChange = useCallback((e) => {
    setFilter(e.target.value);
  }, []);

  return (
    <div>
      <input
        value={filter}
        onChange={handleFilterChange}
        placeholder="Search users..."
      />
      <ul>
        {filteredUsers.map(user => (
          <li key={user.id}>{user.name}</li>
        ))}
      </ul>
    </div>
  );
}

何时使用类组件

// 错误边界必须使用类组件
class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false, error: null };
  }

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

  componentDidCatch(error, errorInfo) {
    // 错误上报
    logErrorToService(error, errorInfo);
  }

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

组件拆分原则

// 不推荐:大而全的组件
function Dashboard() {
  return (
    <div>
      <header>...</header>
      <sidebar>...</sidebar>
      <main>
        <chart>...</chart>
        <table>...</table>
      </main>
      <footer>...</footer>
    </div>
  );
}

// 推荐:拆分成小组件
function Dashboard() {
  return (
    <div>
      <Header />
      <Sidebar />
      <MainContent />
      <Footer />
    </div>
  );
}

function MainContent() {
  return (
    <main>
      <SalesChart />
      <UserTable />
    </main>
  );
}

组件命名规范

// 组件名使用 PascalCase
function UserProfile() { }
class ShoppingCart extends React.Component { }

// 文件名与组件名一致
// UserProfile.js
// ShoppingCart.js

// 私有组件以下划线开头
function _PrivateHelper() { }

Props 设计原则

// 好的 props 设计
function Button({
  children,
  onClick,
  variant = 'primary', // 默认值
  disabled = false,
  size = 'medium'
}) {
  return (
    <button
      className={`btn btn-${variant} btn-${size}`}
      onClick={onClick}
      disabled={disabled}
    >
      {children}
    </button>
  );
}

// 使用
<Button variant="secondary" size="large" onClick={handleClick}>
  Click Me
</Button>

面试要点

  1. 三种构建方式

    • 函数组件:简单、现代、推荐
    • React.createClass:已废弃
    • 类组件:完整功能,但较复杂
  2. 函数组件的优势

    • 代码简洁
    • 没有 this 绑定问题
    • 更容易测试
    • 逻辑复用通过 Hooks
  3. Hooks 的出现

    • 让函数组件拥有状态
    • 替代生命周期方法
    • 更直观的逻辑复用
  4. 类组件的保留场景

    • 错误边界
    • 遗留代码维护
    • 某些特定生命周期需求
  5. 常见面试题

    • React 创建组件的方式有哪些?
    • 函数组件和类组件的区别?
    • 为什么 React.createClass 被废弃?
    • Hooks 能否完全替代类组件?
  6. 现代 React 趋势

    • 函数组件是主流
    • 类组件不再新增特性
    • 服务端组件(Server Components)基于函数组件